代码改变世界

委托的杂七杂八---《clr via c#》笔记

2012-01-31 22:35  海不是蓝  阅读(471)  评论(0编辑  收藏  举报

 

初识委托

委托提供了一种回调的函数机制,委托确保回调的方法是类型安全的,clr最重要的目标之一是类型安全。

非托管的c/c++中回调函数只是个内存地址。

blah blah blah......

什么委托基本语法啊,什么委托回调静态回调实例方法啊!啊!啊!

blah blah blah......

blah blah blah......

blah blah blah......

 

委托揭秘

定义一个委托clr所做的幕后工作

public delegate void Mydelegate(Int32 i);

编译器生成的代码

public class Mydelegate : System.MulticastDelete

{

    public Mydelegate(Int32 i, IntPtr method);   

    public virtual string Invoke(Int32 i);

    public virtual IAsyncResult BeginResult(Int32 i, AsyncCallback callback, Object object);

    public virtual void EndInvoke(IAsyncResult result);

}

 

委托的继承关系

我们的委托继承--->System.MulticastDelete

System.MulticastDelete继承--->System.Delegate

System.Delegate继承--->System.object

 

委托放到什么地方?

有些人把委托放到和class同级的地方,也就是全局,有些人把委托放到class里面,到底怎么放才对?

Jeffrey说:"随便你娃怎么放!你可以把它嵌套到类型中!你也可以把他放到全局范围中定义!委托是类,能够定义类的地方都可以定义委托。"


委托的可访问性

1.把委托放到全局范围,和class同级

public delegate void Mydelegate(Int32 i);

class Program

{

    static void Main(){}

}

这个时候il代码


所以种情况委托是公开的。

2.尝试下把委托定义为私有的

private delegate void Mydelegate(Int32 i);

class Program

{

    static void Main(){}

}

尝试编译代码,但是编译器报错

错误1命名空间中定义的元素无法显式声明为 private、protected 或 protected internal

结果很悲剧!!!!

3.把委托放到类里面

class Program

{

    public delegate void Mydelegate(Int32 i);

    static void Main() { }

}

这个时候委托是公开的,可以在其他地方调用这个委托

public class a

{

    public void test()

    {

        Program.Mydelegate my = test1;

        my(2);

    }

    public void test1(Int32 i) { Console.WriteLine(i); }

}

4.特殊的情况

    public class a

    {

        public delegate void Mydelegate(Int32 i);

        public event Mydelegate Myevent;

    }

当类中有个委托类型的事件,那么外部就不能直接使用委托了,这个时候就相当于委托是字段,而事件是属性。那么就体现了封装性了。

你可以试试在其它类中来访问这个委托,完全不给你机会!只能通过事件。

 

System.MulticastDelete

 

由于所有委托类型都派生自System.MulticastDelete,逼不得已要研究它!!!

它有3个重要的非公共字段,我们定义的委托类型继承了这3个字段。

 

字段

类型

说明

_target

System.Object

委托对象包装一个静态方法的时候为null,实例方法的时候是回调方法要操作的对象,也就是实例方法的this值

_methodPtr

System.IntPtr

一个内部整数值,clr用它标识要回调的方法

_invocationLis

System.Object

一般是null,构造一个委托链时,它可以引用一个委托数组

 

说了字段现在说说2个主要的属性Target和Method

Target返回一个引用,它指向回调方法要操作的对象。

Method要回调方法的内存地址

 

还有就是委托类型的Invoke方法,就是它去调用那个要回调的方法。

委托对象 a,可以写a();也可以直接去a.Invoke();

我疯了才去用a.Invoke(),浪费键盘寿命

 

委托链

童鞋们!委托的+=,-=是不是用得很爽,其实最开始没得这个!让你回到解放前

public static Delegate Combine(Delegate a, Delegate b);

上面那个就是+=的祖宗老大爷!

public delegate void Mydelegate();

class Program

{

    static void Main()

    {

        Mydelegate my1 = new Mydelegate(test1);

        Mydelegate my2 = new Mydelegate(test2);

        Mydelegate my3 = new Mydelegate(test3);

        Mydelegate my = null;

        my = (Mydelegate)Delegate.Combine(my, my1);

        my = (Mydelegate)Delegate.Combine(my, my2);

        my = (Mydelegate)Delegate.Combine(my, my3);

        my();

        Console.WriteLine("------丝袜般的分割线------");

        my = (Mydelegate)Delegate.Remove(my, my1);

        my = (Mydelegate)Delegate.Remove(my, new Mydelegate(test2));

        my();

        Console.Read();

    }

    public static void test1() { Console.WriteLine("1"); }

    public static void test2() { Console.WriteLine("2"); }

    public static void test3() { Console.WriteLine("3"); }

}

上面的代码就是委托链的解放前写法!爽吧!

伟大的微软为了解放我们这些苦命的程序员,就搞了+=和-=这2个操作符。

 

委托链不爽的地方!

如果委托链中一个方法异常或者堵塞很久,那么后面的方法都没法调用!所以默认的委托链调用算法不够健壮。

然后下面是Jeffrey的解决思路,擦 加了个try。我忍!

public delegate void Mydelegate();

class Program

{

    static void Main()

    {

        a a1 = new a();

        Mydelegate my1 = new Mydelegate(a1.test1);

        Mydelegate my2 = new Mydelegate(a1.test2);

        Mydelegate my3 = new Mydelegate(a1.test3);

        Mydelegate my = null;

        my = (Mydelegate)Delegate.Combine(my, my1);

        my = (Mydelegate)Delegate.Combine(my, my2);

        my = (Mydelegate)Delegate.Combine(my, my3);

        Show(my);

        Console.Read();

    }

    static void Show(Mydelegate my)

    {

        foreach (Mydelegate d in my.GetInvocationList())

        {

            try

            {

                d();

            }

            catch (InvalidOperationException e)

            {

                Console.WriteLine(e.Message);

                Console.WriteLine(d.Target);

            }

        }

    }

}

public class a

{

    public void test1() { Console.WriteLine("1"); }

    public void test2() { Console.WriteLine("2"); }

    public void test3() { throw new InvalidOperationException("blah blah blah......"); }

}

 

委托与反射先欠着,我不是机器人

 

****************************

祖贤今天45岁了。