C#基础知识之泛型
泛型在c#中有很重要的位置,对于写出高可读性,高性能的代码有着关键的作用。
其实官方文档说明的很详细,我这边算是做个记录吧
一、什么是泛型?
泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个非常重要的新功能。
泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。您可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。
简单的说,我们在编写程序时,经常遇到两个模块的功能非常相似,只是一个是处理int数据,另一个是处理string数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同。有没有一种办法,在方法中传入通用的数据类型,这样不就可以合并代码了吗?泛型的出现就是专门解决这个问题的。
二、为什么使用泛型?
我们先来看一下下面的代码
static void Main(string[] args)
{
#region 方式一 普通方法
ShowInt();
ShowStr("string1");
#endregion
Console.ReadKey();
}
public static void ShowInt(int num)
{
Console.WriteLine("输出INT型数据:{0}", num);
}
public static void ShowStr(string str)
{
Console.WriteLine("输出String型数据:{0}",str);
}
这两个方法,除了传入的参数不同外,其里面实现的功能都是一样的。在1.1版的时候,还没有泛型这个概念,那么怎么办呢。就有人想到了OOP三大特性之一的继承,C#语言中,所有类型都源自同一个类型,那就是object。其实就是一个装箱拆箱的过程,会损耗一些性能。
static void Main(string[] args)
{
#region 方式二 继承,C#语言中,所有类型都继承自object。
ShowObj();
ShowObj("string2");
#endregion
Console.ReadKey();
}
public static void ShowObj(object obj)
{
Console.WriteLine("输出的类型{0},值{1}",obj.GetType(),obj);
}
微软在2.0的时候发布了泛型。接下来我们用泛型方法来实现该功能。下面是一个简单的泛型示例:
static void Main(string[] args)
{
#region 方式三 泛型
ShowInfo();
ShowInfo("STRING3");
ShowInfo<);
ShowInfo<string>("STRING4");
#endregion
Console.ReadKey();
}
public static void ShowInfo<T>(T para)
{
Console.WriteLine("输出的类型{0},值{1}",para.GetType(),para);
}
三、泛型的特性
使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:
- 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
- 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
- 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
- 您可以对泛型类进行约束以访问特定数据类型的方法。
- 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
四、泛型类型参数
在泛型类型或方法定义中,类型参数是在其实例化泛型类型的一个变量时,客户端指定特定类型的占位符。泛型类无法按原样使用,因为他不是真正的类型,他更像是类型的蓝图,若要使用GenericList<T>,客户端代码必须通过指定尖括号内的类型参数来声明并实例化构造类型。此特定类的类型参数可以是编译器可识别的任何类型,可创建任意数量的构造类型实例,其中每个使用不同的类型参数。在 GenericList<T> 的每个实例中,类中出现的每个 T 在运行时均会被替换为类型参数。 通过这种替换,我们已通过使用单个类定义创建了三个单独的类型安全的有效对象。
GenericList<float> list1 = new GenericList<float>(); GenericList<ExampleClass> list2 = new GenericList<ExampleClass>(); GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();
五、泛型类型参数约束
1、什么是泛型类型参数约束
约束告知编译器类型参数必须具备的功能。在没有约束的情况下,类型参数可以是任何类型,编译器只能假定Object的成员,object是任何.NET类型的最终基类。如果客户端代码尝试使用约束所不允许的类型来实例化类,则会产生编译时错误。通过使用where上下文关键字指定约束。下表列出了其中类型的约束:

某些约束是互斥的。 所有值类型必须具有可访问的无参数构造函数。 struct 约束包含 new() 约束,且 new()约束不能与 struct 约束结合使用。 unmanaged 约束包含 struct 约束。 unmanaged 约束不能与 struct 或 new() 约束结合使用。
2、使用泛型参数约束的原因
通过约束类型参数,可以增加约束类型及其继承层次结构中的所有类型所支持的允许操作和方法调用的数量。 设计泛型类或方法时,如果要对泛型成员执行除简单赋值之外的任何操作或调用 System.Object 不支持的任何方法,则必须对该类型参数应用约束。 例如,基类约束告诉编译器,仅此类型的对象或派生自此类型的对象可用作类型参数。 编译器有了此保证后,就能够允许在泛型类中调用该类型的方法。 基类约束的例子如下:
3、各个泛型参数约束简介
(1)where T:类(类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。)
class Program
{
static void Main(string[] args)
{
#region 基类约束实例
;
;
SchoolGeneric<BeijingSchool> beijingSchool = new SchoolGeneric<BeijingSchool>();
beijingSchool.Add(new BeijingSchool(stuNum,teacherNum, "beijing",true));
beijingSchool.FindSchoolByNum(stuNum);
#endregion
Console.ReadKey();
}
}
class School
{
private int _studentNum;
private int _teacherNum;
private string _schoolName;
public int StudentNum
{
get { return this._studentNum; }
set { this._studentNum = value; }
}
public int TeacherNum
{
get { return this._teacherNum; }
set { this._teacherNum = value; }
}
public string SchoolName
{
get{ return this._schoolName; }
set { this._schoolName = value; }
}
public School(int studentNum,int teacherNum,string schoolName)
{
this.StudentNum = studentNum;
this.TeacherNum = teacherNum;
this.SchoolName = schoolName;
}
}
class BeijingSchool:School
{
private bool isPrivateSchool;
public bool IsPrivateSchool
{
get { return isPrivateSchool; }
set
{
isPrivateSchool = value;
}
}
public BeijingSchool(int stuNum, int teacherNum,string schoolName,bool isPrivateSchool) : base(stuNum, teacherNum, schoolName)
{
this.IsPrivateSchool = isPrivateSchool;
}
}
class SchoolGeneric<T> where T : School
{
T[] TList;
int end;
public SchoolGeneric()
{
];
end = ;
}
public void Add(T school)
{
TList[end] = school;
end++;
}
public void FindSchoolByNum(int StuNum)
{
Console.WriteLine("学生人数满足:{0} 的学校有:", StuNum);
; i < end; i++)
{
if (TList[i].StudentNum == StuNum)
{
Console.WriteLine(TList[i].SchoolName);
}
}
}
}
(2)where T:结构(类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。)
class PersonGeneric<T> where T:struct
{
public void Print(T person)
{
Console.WriteLine("{0}",person);
}
}
class Program
{
static void Main(string[] args)
{
#region where T : struct 类型参数必须是值类型,可以指定除 Nullable<T> 以外的任何值类型
PersonGeneric<int> personAge = new PersonGeneric<int>();
personAge.Print();
PersonGeneric<double> personName = new PersonGeneric<double>();
personName.Print(12.21);
#endregion
Console.ReadKey();
}
}
(3)where T:new()(类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。)
class People<T> where T : Person, System.IComparable<T>, new()
{
// ...
}
(4)where T:<接口名称>(类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。)
interface IMyInterface
{
}
class Dictionary<TKey, TVal> where TKey : IComparable,IEnumerable where TVal : IMyInterface
{
public void Add(TKey key,TVal val)
{
Console.WriteLine("key:{0},val:{1}",key,val);
}
}
class Program
{
static void Main(string[] args)
{
#region where T:new()(类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。)
SplitLine("where T:new()");
#endregion
Console.ReadKey();
}
}
(5)where T:U(为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。也就是说T和U的参数必须一样)
class List<T>
{
void Add<U>(List<U> items) where U : T {/*...*/}
}
六、泛型类
通常,创建泛型类是从现有具体类开始,然后每次逐个将类型更改为类型参数,直到泛化和可用性达到最佳平衡。创建自己的泛型类时,需要考虑以下重要注意事项:
要将哪些类型泛化为类型参数。
通常,可参数化的类型越多,代码就越灵活、其可重用性就越高。 但过度泛化会造成其他开发人员难以阅读或理解代码。
要将何种约束(如有)应用到类型参数(请参阅类型参数的约束)。
其中一个有用的规则是,应用最大程度的约束,同时仍可处理必须处理的类型。 例如,如果知道泛型类仅用于引用类型,则请应用类约束。 这可防止将类意外用于值类型,并使你可在
T上使用as运算符和检查 null 值。是否将泛型行为分解为基类和子类。
因为泛型类可用作基类,所以非泛型类的相同设计注意事项在此也适用。 请参阅本主题后文有关从泛型基类继承的规则。
实现一个泛型接口还是多个泛型接口。
class BaseNode { }
class BaseNodeGeneric<T> { }
// concrete type
class NodeConcrete<T> : BaseNode { }
//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }
//open constructed type
class NodeOpen<T> : BaseNodeGeneric<T> { }
七、泛型方法
泛型方法是通过类型参数声明的方法
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
public static void TestSwap()
{
;
;
Swap<int>(ref a, ref b);
System.Console.WriteLine(a + " " + b);
}
八、泛型接口
为泛型集合类或表示集合中的项的泛型类定义接口通常很有用处。 为避免对值类型的装箱和取消装箱操作,泛型类的首选项使用泛型接口,例如 IComparable<T>而不是 IComparable。 .NET Framework 类库定义多个泛型接口,以将其用于 System.Collections.Generic 命名空间中的集合类。接口被指定为类型参数上的约束时,仅可使用实现接口的类型。 如下代码示例演示一个派生自 GenericList<T> 类的 SortedList<T> 类。 SortedList<T> 添加约束 where T : IComparable<T>。这可使 SortedList<T> 中的 BubbleSort 方法在列表元素上使用泛型 CompareTo 方法。 在此示例中,列表元素是一个实现 IComparable<Person> 的简单类 Person。
public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
{
protected Node head;
protected Node current = null;
protected class Node
{
public Node next;
private T data;
public Node(T t)
{
next = null;
data = t;
}
public Node Next
{
get { return next; }
set { next = value; }
}
public T Data //T as return type of property
{
get { return data; }
set { data = value; }
}
}
public GenericList()
{
head = null;
}
public void AddHead(T t)
{
Node n = new Node(t);
n.Next = head;
head = n;
}
public System.Collections.Generic.IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
{
public void BubbleSort()
{
if (null == head || null == head.Next)
{
return;
}
bool swapped;
do
{
Node previous = null;
Node current = head;
swapped = false;
while (current.next != null)
{
)
{
Node tmp = current.next;
current.next = current.next.next;
tmp.next = current;
if (previous == null)
{
head = tmp;
}
else
{
previous.next = tmp;
}
previous = tmp;
swapped = true;
}
else
{
previous = current;
current = current.next;
}
}
} while (swapped);
}
}
public class Person : System.IComparable<Person>
{
string name;
int age;
public Person(string s, int i)
{
name = s;
age = i;
}
// This will cause list elements to be sorted on age values.
public int CompareTo(Person p)
{
return age - p.age;
}
public override string ToString()
{
return name + ":" + age;
}
// Must implement Equals.
public bool Equals(Person p)
{
return (this.age == p.age);
}
}
class Program
{
static void Main()
{
//Declare and instantiate a new generic SortedList class.
//Person is the type argument.
SortedList<Person> list = new SortedList<Person>();
//Create name and age values to initialize Person objects.
string[] names = new string[]
{
"Franscoise",
"Bill",
"Li",
"Sandra",
"Gunnar",
"Alok",
"Hiroyuki",
"Maria",
"Alessandro",
"Raul"
};
, , , , , , , , , };
//Populate the list.
; x < ; x++)
{
list.AddHead(new Person(names[x], ages[x]));
}
//Print out unsorted list.
foreach (Person p in list)
{
System.Console.WriteLine(p.ToString());
}
System.Console.WriteLine("Done with unsorted list");
//Sort the list.
list.BubbleSort();
//Print out sorted list.
foreach (Person p in list)
{
System.Console.WriteLine(p.ToString());
}
System.Console.WriteLine("Done with sorted list");
}
}
九、泛型和数组
在 C# 2.0 和更高版本中,下限为零的单维数组自动实现 IList<T>。 这可使你创建可使用相同代码循环访问数组和其他集合类型的泛型方法。 此技术的主要用处在于读取集合中的数据。 IList<T> 接口无法用于添加元素或从数组删除元素。 如果在此上下文中尝试对数组调用 IList<T> 方法(例如 RemoveAt),则会引发异常。如下代码示例演示具有 IList<T> 输入参数的单个泛型方法如何可循环访问列表和数组(此例中为整数数组)。
class Program
{
static void Main()
{
, , , , };
List<int> list = new List<int>();
; x < ; x++)
{
list.Add(x);
}
ProcessItems<int>(arr);
ProcessItems<int>(list);
}
static void ProcessItems<T>(IList<T> coll)
{
System.Console.WriteLine
("IsReadOnly returns {0} for this collection.",
coll.IsReadOnly);
foreach (T item in coll)
{
System.Console.Write(item.ToString() + " ");
}
System.Console.WriteLine();
}
}
十、泛型委托
委托可以定义它自己的类型参数。 引用泛型委托的代码可以指定类型参数以创建封闭式构造类型,就像实例化泛型类或调用泛型方法一样,如以下示例中所示:
public delegate void Del<T>(T item);
public static void Notify(int i) { }
Del<int> m1 = new Del<int>(Notify);
C#基础知识之泛型的更多相关文章
- C# 篇基础知识11——泛型和集合
.NET提供了一级功能强大的集合类,实现了多种不同类型的集合,可以根据实际用途选择恰当的集合类型. 除了数组 Array 类定义在System 命名空间中外,其他的集合类都定义在System.Coll ...
- (整理)C#基础知识_泛型的实现
本文是截取自MSDN的文章部分,方便自己查看,原文地址:https://msdn.microsoft.com/zh-cn/library/ms379564(VS.80).aspx 泛型实现 表面上,C ...
- C#基础知识之泛型集合转换为DataTable
在做项目中,遇到了将集合转换为DataTable的使用,在网上看了资料,在这里记录下来,分享. using System; using System.Collections.Generic; usin ...
- C#基础知识之类和结构体
虽然项目中一直在使用类.结构体等类型,仔细琢磨,还真无法系统的说出个所以然.记录一下类.结构体.类和结构体区别 一.类 对于类,大家都特别熟悉.简单的介绍一下类的结构,然后记录一下Class需要注意的 ...
- C#基础知识之类和结构
虽然项目中一直在使用类.结构体等类型,仔细琢磨,还真无法系统的说出个所以然.记录一下类.结构体.类和结构体区别 一.类 对于类,大家都特别熟悉.简单的介绍一下类的结构,然后记录一下Class需要注意的 ...
- [C# 基础知识梳理系列]专题六:泛型基础篇——为什么引入泛型
引言: 前面专题主要介绍了C#1中的2个核心特性——委托和事件,然而在C# 2.0中又引入一个很重要的特性,它就是泛型,大家在平常的操作中肯定会经常碰到并使用它,如果你对于它的一些相关特性还不是很了解 ...
- [C# 基础知识系列]专题九: 深入理解泛型可变性
引言: 在C# 2.0中泛型并不支持可变性的(可变性指的就是协变性和逆变性),我们知道在面向对象的继承中就具有可变性,当方法声明返回类型为Stream,我们可以在实现中返回一个FileStream的类 ...
- C# 基础知识系列- 10 反射和泛型(二)
0. 前言 这篇文章延续<C# 基础知识系列- 5 反射和泛型>,继续介绍C#在反射所开发的功能和做的努力.上一篇文章大概介绍了一下泛型和反射的一些基本内容,主要是通过获取对象的类型,然后 ...
- .NET面试题系列[1] - .NET框架基础知识(1)
很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...
随机推荐
- C#版(击败97.76%的提交) - Leetcode 557. 反转字符串中的单词 III - 题解
版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. Leetcod ...
- WebSocket刨根问底(四)之五子棋大战江湖
有暇,做了个五子棋大战的小游戏送给各位小伙伴! 用到的知识点有: 1.JavaWeb基础知识(懂jsp,servlet足够) 2.JavaScript和jQuery基本用法 3.了解WebSocket ...
- PL/SQL基础语法入门
先前安装了PL/SQL软件 PL/SQL全称为Procedural Language/SQL. PL/SQL也是一种程序语言,叫做过程化SQL语言,是Oracle数据库对SQL语句的扩展 打PL/SQ ...
- 初探Java设计模式1:创建型模式(工厂,单例等)
Java 设计模式 一直想写一篇介绍设计模式的文章,让读者可以很快看完,而且一看就懂,看懂就会用,同时不会将各个模式搞混.自认为本文还是写得不错的,花了不少心思来写这文章和做图,力求让读者真的能看着简 ...
- salesforce lightning零基础学习(十三) 自定义Lookup组件(Single & Multiple)
上一篇简单的介绍了自定义的Lookup单选的组件,功能为通过引用组件Attribute传递相关的sObject Name,捕捉用户输入的信息,从而实现搜索的功能. 我们做项目的时候,可能要从多个表中获 ...
- 前端笔记之jQuery(下)事件&节点操作&净位置&拖拽&页面卷动值&遍历JSON
一.监听事件大全 1.1 JavaScript事件 onblur 元素失去焦点 onchange 用户改变域的内容 onclick 鼠标点击某个对象 ondblclick 鼠标双击某个对象 onfoc ...
- leetcode — convert-sorted-list-to-binary-search-tree
import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Source : https://o ...
- RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.2->WinForm版本新增新的角色授权管理界面效率更高、更规范
角色授权管理模块主要是对角色的相应权限进行集中设置.在角色权限管理模块中,管理员可以添加或移除指定角色所包含的用户.可以分配或授予指定角色的模块(菜单)的访问权限.可以收回或分配指定角色的操作(功能) ...
- 离线批量数据通道Tunnel的最佳实践及常见问题
基本介绍及应用场景 Tunnel是MaxCompute提供的离线批量数据通道服务,主要提供大批量离线数据上传和下载,仅提供每次批量大于等于64MB数据的场景,小批量流式数据场景请使用DataHub实时 ...
- Linux 项目上线管理 MAVEN + expect 一台机器管理所有机器的应用程序
一.目的 在一台服务器上面管理所有机器的应用程序. 设想是通过一条命令能够知道所有应用程序是否running 如果not running 查看具体项目的log 跟踪具体原因,程序问题汇报相关负责人 二 ...