细说委托

在正式介绍委托之前,我想下看看生活中委托的例子——生活中,如果我们需要打官司,在法庭上是由律师为我们辩护的,然而律师执行的是当事人的陈词,这时候律师就是一个委托对象,当事人委托律师这个对象去帮自己辩护。这就是我们生活中委托的例子的。然而C#中委托的概念也就好比律师对象(从中可以得出委托是一个类,因为只有类才有对象的概念,从而也体现了C#是面向对象的语言)。 
介绍完生活中委托是个什么后,现在就看看C#中的委托怎样和生活中的对象联系起来的,C#中的委托相当于C++中的函数指针(如果之前学过C++就知道函数指针是个什么概念的了),函数指针是用指针获取一个函数的入口地址,然后通过这个指针来实现对函数的操作。C#中的委托相当于C++中的函数指针,也就说两者是有区别的:委托是面向对象的,类型安全的,是引用类型(开始就说了委托是个类),所以在使用委托时首先要 声明委托类型——>有一个方法包含了执行的代码——>创建一个委托实例——>调用该委托实例。下面就具体看下如何使用委托的: 
1、 声明委托类型,委托被声明为: 
public delegate void AddDelete (int a,int b); 上述代码指出,如果要创建AddDelete的一个实例,需要带两个参数(两个都是int 类型)的方法,而且该方法需要有一个 void返回类型(该方法什么都不返回)。 2、 有一个方法包含了执行的代码 
public static void Add(int a, int b) 
        { 
            Console.WriteLine(a+b);         } 
3、 创建一个委托实例 
AddDelete _adddelete = new AddDelete(Add); 
创建委托实例,使用了new 关键字,说明委托也是类,将方法名Add作为参数绑定到该委托实例,也就是将方法Add指派给AddDelete委托,并将该引用赋给_adddelete对象,也就表示_adddelete对象保存了指向Add方法的引用,以此实现了对Add的回调。由此可见,委托表示了对其回调方法的签名,可以将方法当做参数进行传递,并根据传入的方法来动态的改变方法调用,只要为委托提供相同的签名的方法,都可以与委托绑定。 4、 调用该委托实例 
_adddelete(1, 2); 
 完整代码如下: 
    class Program     { 
        public delegate void AddDelete(int a,int b);         static void Main(string[] args)         { 
            //创建委托实例,使用了new 关键字,说明委托也是类,将方法名Add作为参数绑定到该委托实例             AddDelete _adddelete = new AddDelete(Add); 

//调用委托实例             _adddelete(1, 2);             Console.ReadLine();         }  
        public static void Add(int a, int b)         { 
            Console.WriteLine(a+b);         } 

坦白讲,如果仅仅是为了显示上述a+b的输出,也没有必须使用委托,那么为什么不直接调用方法呢?答案存在于我们最开始的那个让律师来帮当事人辩护的例子中,不能仅仅由于当事人需要打官司这件事件发生,就意味着当事人懂法律,有时候,你需要给出一些指令(比如我需要打官司),将职责委托给别人(比如律师)。 
应该强调的一点的时候,在软件世界中,没有对象 “打官司”这样的事情发生,经常都会发现这种情况,委托实例被调用时,最初创建委托实例的对象仍然存在的,也就是该对象自己也写一个打官司的方法,而委托相当于指定一些代码在特定的时间执行,那时,你也许已经无法(或者不想)更改要执行的代码,比如张三想打官司,直接调用已经存在的方法,并不想在张三对象内部重新写该方法,委托的实质就是间接完成某种操作,许多面向对象编程技术都在做同样的事情,我们看到,这增大了复杂性(看看为了输出a+b这点事,用了多少代码),但是同时也增加了灵活性。 
相信通过上面两部分大家也明白了委托是个什么东西以及C#中为什么要引入委托这个概念。现在就总结下引入委托后到底作用在那里的? 从上面的委托代码中可以发现,引入委托后,编程人员可以把方法的引用封装在委托对象中(把过程的调用转化为对象的调用,充分体现了委托加强了面向对象编程的思想。),然后把委托对象传递给需要引用方法的代码,这样在编译的过程中我们并不知道调用了哪个方法,这样一来,C#引入委托机制后,使得方法声明和方法实现的分离,充分体现了面向对象的编程思想。 
匿名方法 
匿名方法就是没有名字的方法(函数),既然没有名字,就是说只有在定义的时候能调用,在其他地方就不能调用了(没有名字啊,那就找不到嘛)。为什么要用到匿名方法呢?调用函数是需要花销的,但有时候调用的一些方法很短小(例如一句话方法)、只完成很少的功能,这个时候就有点得不偿失了,此时就可以定义一个匿名方法来完成这个功能了,而匿名方法作为内联代码,花销相对小很多。匿名方法都是和委托连在一起用的(以后还有lambda表达式),以前创建委托实例时需要传递一个函数名给它作为参数,现在可以通过匿名方法直接把一段代码传进去了。 
定义匿名方法:用到delegate关键字,如:delegate(int a, int b){return a > b ? a : b }   代码定义了一个匿名方法,该方法的参数是 int a 和 int b ,方法体是 {return a > b ? a : b}。如果只是定义一个匿名方法没有意义,因为定义完过后你就再也不能用这个匿名方法,所以匿名方法要在定义的时候就马上使用。一般来说就是初始化委托了。 
上面 a+b 的例子 如何使用匿名方法,如下,可以看到这样就不需要再定义一个Add函数了, 
    class Program     { 
        public delegate void AddDelete(int a,int b);         static void Main(string[] args) 


            //创建委托实例,使用了new 关键字,说明委托也是类,将方法名Add作为参数绑定到该委托实例             AddDelete _adddelete = delegate(int a, int b) { Console.WriteLine(a + b); };             //调用委托实例             _adddelete(1, 2);             Console.ReadLine();         }  
    } 
委托在Winform中的使用 
1、如下所示, 如你所见,我现在创建了一个简陋的Form,其中放置了一个Lable控件lable1,一个Button控件button1,下面,开始code: 
 
 
        private void button1_Click(object sender, EventArgs e)         { 
            //新建一个线程,该线程调用test方法             Thread t1 = new Thread(test);             t1.Start();         }  
        public void test()         { 
            MethodInvoker mi = delegate() { 
                this.label1.Text = "子线程中也可以显示我";             }; 
            label1.BeginInvoke(mi);         } 
使用了BeginInvoke,我们查看BeginInvoke 的定义:可知,该方法传入的参数是 Delegate 回调方法,也就是 BeginInvoke 回调了一个委托方法,该方法具体实现由开发者自己写。  
 
 
而 winform 本身声明了一个 委托类型 MethodInvoker 
namespace System.Windows.Forms

 

 

    // 摘要:  
    //     表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。     public delegate void MethodInvoker(); }      
所以,我们调用 BeginInvoke的时候,可以让他执行 一个匿名方法  
            MethodInvoker mi = delegate() { 
                this.label1.Text = "子线程中也可以显示我";             }; 
            label1.BeginInvoke(mi); 
 
当然,我们也可以这样写  
            label1.BeginInvoke((MethodInvoker)delegate()             { 
                this.label1.Text = "子线程中也可以显示我";             });  
那么 我们 自己声明一个 委托 可以吗?当然也是可以的。 
public delegate void Methodq(); 
然后调用 使用  
            label1.BeginInvoke((Methodq)delegate()             { 
                this.label1.Text = "子线程中也可以显示我";             });  
我们回头再看看 BeginInvoke 的定义,他是public class Control 类中的一个方法,一个返回类型为
IAsyncResult,传入参数为委托  
public IAsyncResult BeginInvoke(Delegate method);   
而 IAsyncResult 又是做什么的?查看定义如下,IAsyncResult为一个一步操作的接口,下一节课,将详细讲解,异步编程。  
    // 摘要:  
    //     表示异步操作的状态。     [ComVisible(true)] 
    public interface IAsyncResult


        // 摘要:  
        //     获取用户定义的对象,它限定或包含关于异步操作的信息。         // 
        // 返回结果:  
        //     用户定义的对象,它限定或包含关于异步操作的信息。         object AsyncState { get; }         //         // 摘要:  
        //     获取用于等待异步操作完成的 System.Threading.WaitHandle。         // 
        // 返回结果:  
        //     用于等待异步操作完成的 System.Threading.WaitHandle。         WaitHandle AsyncWaitHandle { get; }         //         // 摘要:  
        //     获取一个值,该值指示异步操作是否同步完成。         // 
        // 返回结果:  
        //     如果异步操作同步完成,则为 true;否则为 false。         bool CompletedSynchronously { get; }         //         // 摘要:  
        //     获取一个值,该值指示异步操作是否已完成。         // 
        // 返回结果:  
        //     如果操作完成则为 true,否则为 false。         bool IsCompleted { get; }     } 

转载自:http://wenku.baidu.com/link?url=G0cQBAziuoxCTg2U4EgGHbQX500tNwHGgLJ-nvrz4Vs0jhsfaXEkFqxhS95AkJkkQSC3ZOm7NToI3Tes1xbh-2u8HAb6ZeAWjuAylIbJHN3

posted @ 2016-06-07 17:41  liuxixi  阅读(249)  评论(0编辑  收藏  举报