泛型与委托
小结:
一、泛型【代码重用/算法重用】
1.优点:
a.源代码保护:使用泛型算法时不需要访问算法的源代码
b.类型安全性
c.更加清晰的代码
d.更好的性能:CLR不再需要执行任何装箱、拆箱操作
2.常用的泛型集合类/泛型容器
a.List<T>
b.Dictionary<TKey,TValue>
c.Stack<T>
d.Queue<T>
3.代码爆炸
使用泛型类型参数的一个方法在进行JIT编译时,CLR获取方法的IL,替换指定的类型参数,然后针对那个方法在指定数据类型上的操作创建特有的本地代码。这是泛型的主要特性之一,然而,这样做有一个缺点:CLR要为每种不同的方法/类型组合生成本地代码。将这个现象称为“代码爆炸”,最终会造成应用程序的工作集显著增大,从而损害性能。
幸运的是,CLR内建了一些优化措施,能够缓解代码爆炸:
a.假如为一个特定的类型实参调用了一个方法,以后再次使用相同的类型实参来调用这个方法,那么CLR只会为这种方法/类型组合编译一次代码。
b.CLR认为所有引用类型实参都是完全相同的,所以代码能够共享。例如,CLR为List<String>的方法编译的代码可以用于List<Stream>的方法。
----但对于值类型,CLR必须专门为那个值类型生成本地代码,即使两个值类型具有相同的大小(比如Int32和UInt32两者都为32位),CLR仍然无法共享代码。
二、委托
1.协变与反协变
将一个方法绑定到一个委托时,C#和CLR都允许引用类型的协变与反协变
协变:指的是一个方法的返回类型可以是委托的返回类型的一个派生类型
反协变:指的是一个方法的参数类型可以是委托的参数类型的基类型
例子: public delegate Object TestCallBack(String s);
public static String SomeMethod(Object st) { return "hi"; }
TestCallBack call = SomeMethod("tty");//这是允许的
----协变与反协变只能用于引用类型,不能用于值类型或void,所以不能把下面的方法绑定到TestCallBack委托:
public static Int32 SomeMethod(Object st);
2.委托的本质
委托本质上是一个类,该类中含一个构造函数、Invoke、BeginInvoke和EndInvoke四个方法,形成的IL Code如下图所示:
构造函数含两个参数,一个是对象的引用,另一个是引用该对象中的回调方法的一个整数
所有委托都继承自System.MulticastDelegate, System.MulticastDelegate类继承自System.Delegate, System.Delegate继承自System.Object。
a. 委托定义了方法的类型,使得可以将方法当作另一个方法的参数进行传递,这种将方法动态的赋给参数的做法,可以避免在程序中大量使用If---Else(Switch)语句,同时使得程序具有更好的可扩展性
b. 可以将多个方法绑定到同一个委托变量身上,当调用此变量时,可以依次调用所有绑定的方法
3.委托链
C#编译器为委托类型的实例提供了运算符+=和-=重载,这两个运算符分别调用Delegate.Combine和Delegate.Remove,从而简化了源代码
在创建好一个委托对象链时,调用该委托后会自动调用委托链上的所有对象的方法,带来方便之外也有它的局限性:
a.除最后一个回调方法的返回值之处,其他回调方法的返回值都会被丢弃
b.如果被调用的委托中有一个抛出了异常或阻塞了相当长一段时间,就会停止调用后续所有对象的方法
有些时候我们需要对链上的每个对象的方法控制的更多,Delegate中定义了一个获取委托数组的虚方法,如下:
public abstract class Delegate : ICloneable, ISerializable { public virtual Delegate[] GetInvocationList(); ... } public abstract class MulticastDelegate : Delegate { public override sealed Delegate[] GetInvocationList(); ... }
这样,当我们创建一个委托链时,我们就可以通过GetInvocationList方法获取该链上的每一个委托对象,然后就可以为每个对象中的方法加上我们自己的控制了
一个简单例子:
public delegate void Change(Int32 x); class Program { public void Add1(Int32 x1) { num += x1; } public void Add2(Int32 x2) { num += x2; } public void Add3(Int32 x3) { num += x3; } } 调用: Program p = new Program(); Change cal = null;//声明一个空委托 cal = p.Add1; cal += p.Add2; cal += p.Add3; if (cal == null) { return; } Delegate[] delist = cal.GetInvocationList(); foreach (Change item in delist)//可以获取每个回调方法的返回值了 { //针对每个委托对象的控制代码 try { ... } catch(InvalidOperationException e) { ... } }