泛型与委托

小结:

一、泛型【代码重用/算法重用】

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)
                {
                    ...
                }
            }

 

posted on 2013-03-02 10:17  Gcam  阅读(331)  评论(0编辑  收藏  举报

导航