泛型(把类型当参数)的优点:
1.避免装箱拆箱
2.避免定义大量的方法,可以存储各种类型的元素
3,泛型可以最大限度的重用代码,保护类型安全以及提高性能
4.泛型最常见的用途是创建集合类
5.NET Framework 类库在 System.Collections.Generic 命名空间中包含几个新的泛型集合类。应尽可能地使用这些类来代替普通的类,如 System.Collections 命名空间中的 ArrayList。
您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
6.可以对泛型类进行约束以访问特定数据类型的方法。
7.关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
尽量使用泛型约束 避免别人使用时出错
打印引用类型时就是调用它的ToString
方法的调用时:Printype<int>(5);int为泛型的参数,它的括号一定是尖括号。5 时参数的实参,它的括号为圆括号
类型参数命名准则
务必使用描述性名称命名泛型类型参数,除非单个字母名称完全可以让人了解它表示的含义,而描述性名称不会有更多的意义。
考虑使用 T 作为具有单个字母类型参数的类型的类型参数名。
务必将“T”作为描述性类型参数名的前缀。
考虑在参数名中指示对此类型参数的约束。例如,可以将带有 ISession 约束的参数命名为 TSession。
泛型方法:
static void Main(string[] args)
{
PrintType<int>(5);
PrintType<bool>(true);
PrintType<string>("泛型真伟大");
PrintType(1.2); //省略类型参数,编译器会自动推导出是double类型,这里要注意,编译器是根据参数的类型推导出类型参数的,如果方法没有参数,编译器是无法推导出来的。
}
static void PrintType<T>(T i)
{
Console.WriteLine("类型:{0,-20} 值:{1}",i.GetType (),i);
}
在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。
下表列出了六种类型的约束:
约束 说明
T:结构 类型参数必须是值类型。可以指定除 以外的任何值类型。
T:类 类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。
T:new() 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。
T:结构
类型参数必须是值类型。
static void Main(string[] args)
{
PrintType<int>(5); //正确的写法
PrintType<string>(“泛型真伟大”); //错误的写法
}
static void PrintType<T>(T i) where T: struct
{
Console.WriteLine("类型:{0,-20} 值:{1}",i.GetType (),i);
}
T:类
类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。
static void Main(string[] args)
{
PrintType<int>(5); //错误的写法
PrintType<string>(“泛型真伟大”); //正确的写法
}
static void PrintType<T>(T i) where T: class
{
Console.WriteLine("类型:{0,-20} 值:{1}",i.GetType (),i);
}
T:new()
类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
class Program
{
static void Main(string[] args)
{
PrintType<Demo>(new Demo());
}
static void PrintType<T>(T i) where T: new()
{
Console.WriteLine("类型:{0,-20} 值:{1}",i.GetType (),i);
}
}
class Demo
{
}
T:<基类名>
类型参数必须是指定的基类或派生自指定的基类。
class Program
{
static void Main(string[] args)
{
PrintType<Demo>(new Demo1()); //正确
PrintType<Demo>(new Demo()); //正确
PrintType<Demo1>(new Demo1()); //正确
PrintType<Demo2>(new Demo2()); //错误
}
static void PrintType<T>(T i) where T:Demo
{
Console.WriteLine("类型:{0,-20} 值:{1}",i.GetType (),i);
}
}
class Demo
{}
class Demo1 : Demo
{}
class Demo2
{}
T:<接口名称>
类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
class Program
{
static void Main(string[] args)
{
PrintType<IDemo>(new Demo()); //正确
PrintType<Demo>(new Demo()); //正确
}
static void PrintType<T>(T i) where T : IDemo
{
Console.WriteLine("类型:{0,-20} 值:{1}", i.GetType(), i);
}
}
interface IDemo
{ }
class Demo : IDemo
{ }
T:U
为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。
class Program
{
static void Main(string[] args)
{
PrintType<IDemo,Demo>(new Demo()); //错误
PrintType<Demo,IDemo>(new Demo()); //正确
PrintType<Demo, Demo>(new Demo()); //正确
}
static void PrintType<T,U>(T i) where T : U
{
Console.WriteLine("类型:{0,-20} 值:{1}", i.GetType(), i);
}
}
interface IDemo
{ }
class Demo : IDemo
{ }
可以对同一类型参数应用多个约束,并且约束自身可以是泛型类型
如下所示:
static void PrintType<T>(T i) where T : class ,new ()
{
Console.WriteLine("类型:{0,-20} 值:{1}", i.GetType(), i);
}
泛型类封装不是特定于具体数据类型的操作。泛型类最常用于集合,如链接列表、哈希表、堆栈、队列、树等。像从集合中添加和移除项这样的操作都以大体上相同的方式执行,与所存储数据的类型无关。
一般情况下,创建泛型类的过程为:从一个现有的具体类开始,逐一将每个类型更改为类型参数,直至达到通用化和可用性的最佳平衡。
创建您自己的泛型类时,需要特别注意以下事项:
将哪些类型通用化为类型参数。
通常,能够参数化的类型越多,代码就会变得越灵活,重用性就越好。但是,太多的通用化会使其他开发人员难以阅读或理解代码。
如果存在约束,应对类型参数应用什么约束。一条有用的规则是,应用尽可能最多的约束,但仍使您能够处理必须处理的类型。例如,如果您知道您的泛型类仅用于引用类型,则应用类约束。这可以防止您的类被意外地用于值类型,并允许您对 T 使用 as 运算符以及检查空值。
是否将泛型行为分解为基类和子类。由于泛型类可以作为基类使用,此处适用的设计注意事项与非泛型类相同。
是否实现一个或多个泛型接口。