运行时,参数类型检查。咋一看,这似乎违背了上面说到的原则,不是说要让错误的检测尽量提到编译期间,由JIT来完成么;其实并不矛盾,运行时参数的检测是为了提高代码的性能,而不是去进行异常的处理。有时候,提供的参数提供了更强大的功能,我们需要在运行时判断其类型,因为编译期间无法做到这一点(泛型的实例化是基于编译期类型而非运行期的),并利用这种类型的功能。下面的例子可以说明:
public class ReverseEnumerable<T>:IEnumerable<T> { private class ReverseEnumerator:IEnumerator<T> { //这是一个迭代器类,里面包括一个用于存放迭代数据的IList<T> 和一个int类型的游标 } IEnumerable<T> source; IList<T> ori; //构造函数 public IEnumerator<T> GetEnumerator() { if(ori==null) { if(source is IList<T>) ori=source as IList<T>; if(source is ICollection<T>) ori= new List<T>(source.Count); //在上面的两种情况可以减少开销 //else copy source to ori } return new ReverseEnumerator(ori); } }
确保泛型支持可销毁对象
public void GetSomeThing() { T driver = new T(); using(driver as IDisposable) { driver.Dowork(); } }
使用委托类型作为方法的参数,减少代码量。
下面给出一个示例,这是该书上作者给的一个示例,个人觉得蛮有启发性.
实体类Point
public class Point { public double X { get; private set; } public double Y { get; private set; } public Point(int x, int y) { this.X=x; this.Y=y; } public Point(TextReader reader) { //do sonmething } }
操作类Action
public delegate TOutput Func<T1,T2, TOutput>(T1 arg1, T2 arg2); public static IEnumerable<TOutput> Merge<T1,T2, TOutput>(IEnumerable<T1> left,IEnumerable<T2> right, Func<T1,T2, TOutput> generator) { IEnumeator<T1> ls = left.GetEnumerator(); IEnumeator<T2> rs = rigth.GetEnumerator(); while(ls.MoveNext()&&rs.MoveNext()) yield return generator(ls.Current, rs.Current); }
使用泛型元组代替ref和out参数实现多个返回值。
熟悉C/C++开发的同仁应该经常用到指针或者引用类型的变量作为参数实现函数的多返回值。C#中为了向下兼容,引入了ref和out关键字,但是并不推荐使用。
首先,破坏了方法的单入口单出口的原则;然后,它无法对数据类型作隐式转化,因此难以实现多态,如:
public class Deliver:Base { } Base a = new Deliver(); Deliver b = new Deliver(); Function(ref (Deliver)a); Function(ref (Base)b);
使用泛型元组就可以解决这个问题,同时也符合单入口单出口的原则。
我们可以返回一个有所有返回值组成的一个泛型Tupe
using CityTemperature = Tuple<string,decimal>; public static CityTemperature Fun(string s) { //.... }