在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1.基本概念.NET2.0新增的最大的特性是泛型。 我们先来看下定义在System.Collections.Generic下的List<T>: public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable List类后边的紧跟着一个<T>,T被称为类型参数(type parameter),是一个真实实参的占位符,表明该类未定义实际的数据类型。 在泛型出现以前,只能通过传递object类型的参数或者返回object类型来实现通用性的方法。
int CompareTo(object obj) 由于System.Object是所有对象的基类,存在任意类型到object的隐式转换,所以可以向CompareTo方法传递任意类型的参数。 public int CompareTo(object value) { if (value == null) { return 1; } if (!(value is int)) { throw new ArgumentException(Environment.GetResourceString("Arg_MustBeInt32")); } int num = (int)value; if (this < num) { return -1; } if (this > num) { return 1; } return 0; } 再看int对泛型接口IComparable<T>的实现: public int CompareTo(int value) { if (this < value) { return -1; } if (this > value) { return 1; } return 0; } 只能向方法传递一个int类型,实现了编译时的安全同时避免了拆箱和装箱的操作。 再来举一个例子。 场景:向列表添加若干数字,计算数字的和。
ArrayList list = new ArrayList(); list.Add(1); list.Add(2); list.Add(3.0); int count = 0; for (int i = 0; i < list.Count; i++) { count += (int)list[i]; } 这段代码编译的时候能通过,运行时会报InvalidCastException异常,类型转换失败。 ArrayList的Add方法的签名为:Add(object value),所以每次使用Add方法向列表添加int对象时,先要进行装箱操作。 再来看使用List<T>的实现: List<int> list = new List<int>(); list.Add(1); list.Add(2); //list.Add(3.0); int count = 0; for (int i = 0; i < list.Count; i++) { count += list[i]; } 注释掉的那行在编译时报错,方法参数不匹配。 我们使用int来替换T,表示list中的所有T都替换为int类型。 3.泛型约束假如你编译以下代码: T Foo<T>()
{
return new T();
}
编译的时候会提示错误:“变量类型“T”没有 new() 约束,因此无法创建该类型的实例 ”。 T Foo<T>() where T : new() { return new T(); } 此处通过where对泛型添加了约束。以下为可以添加的约束类型(MSDN):
示例: T Foo<T>(T t1,T t2) where T :IComparable<T> { if (t1.CompareTo(t2) > 0) { return t1; } return t2; } 很显然的只有对象实现了IComparable<T>接口,才能在对象的实例上调用CompareTo方法。 4.泛型方法泛型类中的方法定义了自己的类型参数时,才能作为一个泛型方法。 public class Test<T> { public T GetInster() { return default(T); } public TValue GetFirstItem<TValue>(TValue[] list) { if (list == null) { throw new ArgumentNullException(); } if (!list.Any()) { throw new ArgumentException(); } return list[0]; } } 编译器在使用泛型方法时,能够通过变量的数据类型自动推断要使用的类型,使用上面的GetFirstItem作为示例: int[] list = new int[] { 1, 2, 3 }; GetFirstItem<int>(list); GetFirstItem(list); 第三行的方法根据参数的类型,自动的推导出类型参数为int。 5.泛型的协变和逆变泛型委托和泛型接口的类型参数可以标记为协变性量或者逆变量, 逆变量:泛型类型参数可以从一个基类变为该类的派生类,使用in关键字标记类型参数,逆变量只能出现在输入位置:方法的参数或者set访问器中的方法。 协变量:泛型类型参数可以从一个派生类变为该类的基类,使用out关键字标记类型参数,协变量只能出现在输出位置:方法的返回值。 对以下的泛型委托类型定义: delegate TResout Func<in T, out TResout>(T t); T被申明为逆变量,TResout被申明为协变量。 MyFunc<object, string> fn1 = s => s.ToString(); MyFunc<string, object> f2 = fn1; 所以在需要一个object对象参数时,可以传递一个string类型对象。 6.其他6.1泛型反射 查看 typeof(List<int>).ToString() 可以发现泛型的类型名为“System.Collections.Generic.List`1[System.Int32]”。 6.2泛型类的构造函数 针对不同的类型参数,泛型类只在第一次调用的时候实时编译,所以第一次调用一个静态类会造成性能损失,以后再调用这个参数就能直接获取了。同时所有的引用类型实参都只会编译一次,因为所有的引用类型都是指向托管堆的指针。假如在某个泛型类中定义了静态字段或者静态构造器,针对不同类型的泛型实现,都拥有各种的静态字段, 6.3泛型实例的默认值 可以通过default(T)来设置泛型实例的默认值,类似逻辑为T是typeof(T).IsValueType ? 0 : null(此处的0是指将值类型的所有位设置为0)。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论