从.NET2.0起,C#引入了泛型的概念。从代码编写的角度看,无疑提高了代码的编写效率;从编译器的工作来看,提高了代码的安全性,泛型的引入避免了几乎所有的运行时的异常,在以前的版本中,系统需要对类型进行检查,对这些异常进行处理,包括栈审核和栈解退,对性能造成影响;同时,在以前版本的Framework中,强制使用System.Object类型,只有在使用之前才将其动态强制转换为需要的类型,这个过程涉及到拆箱和装箱的操作,可能对性能,尤其是使用值类型的性能造成巨大的影响。
编译器对参数为引用类型和值类型的泛型的处理是不同的:(有个概念:所有参数都已经明确给出的泛型类型叫做封闭泛型类型;而仅仅给出部分的叫做开放泛型类型)
1.引用类型。被JIT编译后只生成一份代码,因为所有的引用泛型在申请空间的时候,都是申请一个引用所占的空间。
2.值类型。首先,JIT将创建一个新的IL类,用来表示该封闭类型。(即,根据使用的参数类型创建一个具体值类型的封闭类型);然后,编译成指令。这里并不是在某个类加载时生成完整的指令,而是保存为两部分:一是为每种用值类型作为参数的封闭泛型类型保存一份IT定义的副本(即上面的);二是为每种值类型作为参数的封闭类型保存一份保存一份所调用的方法的机器码的副本。这样的在创建单个某个值类型的对象时,会增加内存,但是避免了装箱和拆箱的操作,降低了值类型的代码和数据所占的空间;同时,对于多个同值类型的对象时,将降低额外生成的IL代码量(因为只需要一份)。
每个System.Collections命名空间中的原有类或接口的新的或者改进版本的位于System.Collections.Generics中,同时也新增了几个:(注意:“扩展”代表是从原来的类中继承而来,“替换”代表由于原有接口没有包含新接口的签名,新接口的签名方法无法与原来接口保持一致,而没有继承)
原有类型 | 新类型 | 更改方式 |
ArrayList | List<T> | 替代 |
Stack | Stack<T> | 替代 |
Hashtable | Dictionary<K,V> | 替代 |
Query | Query<T> | 替代 |
无 | LinkedList<T> | 新增 |
无 | SortedList<T> | 新增 |
IList | IList<T> | 扩展 |
IDictionary | IDictionary<T> | 替代 |
IEmumerable | IEmumerable<T> | 扩展 |
IComparer | IComparer<T> | 替代 |
ICollection | ICollection<T> | 替代 |
无 | IEquateable<T> | 新增 |
无 | IEqualityComparer<T> | 新增 |
下面说说几点建议:
1.再重写Equals方法时,实现IEquatable<T>接口。
public interface IEquatable<T> { bool Equals(T other); }
2.若需对定义在其他类库中的类型进行比较,请实现IEqualityComparer<T>。
/**使用Default属性,将检查参数T是否实现了IEquatable<T>接口。若实现,则返回使用了该类型的接口,否则使用System.Object的。*/ public class MyComparer:EqualityComparer<MyClass> { public override bool Equals(MyClass x, MyClass y) { return EqualityComarer<MyClass>.Default.Equals(x,y); } public override int GetHashCode(MyClass c) { return EqualityComparer<MyClass>.Default.GetHashCode(c); } }
3.使用泛型委托
使用泛型委托可以在不同的情况下,传入不同函数指针。
public delegate TOutput Converter<TInput, TOutput>(TInput input); //创建一个Convert委托 public static IEnumerable<TOutput> Transform<TInput, TOutput>(IEnumerable<TInput> theCol, Converter<TInput,TOutput> transformer) { foreach(TInput source in theCol) yield return transformer(source); }
泛型委托还简化了事件相关的代码。
public delegate void EventHandler<T>(object sender, T args) where T:EventArgs; public event EventHandler<T> OnRaiseMyEvent;
4.坚持最小化约束的原则。例如在默认构造函数约束时,可以将new()约束用default()的调用来代替,该操作符用来将变量初始化为默认值(注意不是去调用默认的构造函数)
public delegate bool Predicate<T>(T value); public static T MyMethodWithDefault<T>(this IEnumeraable<T> sequence, Predicate<T> test) { foreach(T value in squence) { if(test(value)) return value; return default(T); } } /** *默认的构造函数约束是没有必要的,因为并不需要每个使用该方法的类都必 *需要有默认的构造函数,这样会大大减少该函数的使用范围,也未JIT增加 *了不必要的检查工作 */ public static T MyMethodWithOutDefault<T>(this IEnumeraable<T> sequence, Predicate<T> test) where T:new() { foreach(T value in squence) { if(test(value)) return value; return new T(); } }