寄托知识总括,委托的介绍


1.什么是委托,为什么要使用委托

C#委托的介绍(delegate、Action、Func、predicate)

 

我正在埋头苦写程序,突然想喝水,但是又不想自己去掉杯水而打断自己的思路,于是我就想让女朋友去给我倒水。她去给我倒水,首先我得让她知道我想让她干什么,通知她之后我可以继续写自己的程序,而倒水的工作就交给了她。这样的过程就相当于一个委托。

  委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。事件是一种特殊的委托。

委托概述

将方法调用者和目标方法动态关联起来,委托是一个类,所以它和类是同级的,可以通过委托来掉用方法,不要误以为委托和方法同级的,方法只是类的成员。委托定义了方法的类型(定义委托和与之对应的方法必须具有相同的参数个数,并且类型相同,返回值类型相同),使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

 

在程序过程中,当程序正在处理某个事件的时候,我需要另外的程序代码去辅助处理一些事情,于是委托另一个程序模块去处理,而委托就可以达到这种目的,我可以利用委托通知另外的程序模块,该去调用哪个函数方法。委托其实就起到了这样一个作用,将函数签名传递到了另一个函数中。或许这样讲还是有些模糊,看看后面的具体实例。

  1.委托的声明

基础委托(Delegate)

在.Net中声明委托使用关键词delegate,委托具有多种使用方式(以下均为同步委托调用):

 1     /// <summary>
 2     /// 普通委托基础调用方式(同步委托)
 3     /// </summary>
 4     public class Delegates
 5     {
 6         /// <summary>
 7         /// 定义有参无返回值委托
 8         /// </summary>
 9         /// <param name="i"></param>
10         public delegate void NoReturnWithParameters(string o);
11         /// <summary>
12         /// 构造函数实例化
13         /// </summary>
14         public void DemoOne()
15         {
16             NoReturnWithParameters methord = new NoReturnWithParameters(this.Test);
17             methord.Invoke("One-ok");
18         }
19         /// <summary>
20         /// 赋值对象
21         /// </summary>
22         public void DemoTwo()
23         {
24             NoReturnWithParameters methord = this.Test;
25             methord.Invoke("Two-ok");
26         }
27         /// <summary>
28         /// DotNet 2.0 
29         /// </summary>
30         public void DemoThree()
31         {
32             NoReturnWithParameters methord = new NoReturnWithParameters(
33                 delegate (string o)
34                      {
35                          Console.WriteLine("有参无返回值:{0}", o);
36                      }
37             );
38             methord.Invoke("Three-ok");
39         }
40         /// <summary>
41         /// DotNet 3.0 
42         /// </summary>
43         public void DemoFour()
44         {
45             NoReturnWithParameters methord = new NoReturnWithParameters(
46                 (string o) =>
47                     {
48                         Console.WriteLine("有参无返回值:{0}", o);
49                     }
50             );
51             methord.Invoke("Four-ok");
52         }
53         /// <summary>
54         /// 委托约束
55         /// </summary>
56         public void DemoFive()
57         {
58             NoReturnWithParameters methord = new NoReturnWithParameters(
59                 (o) =>
60                 {
61                     Console.WriteLine("有参无返回值:{0}", o);
62                 }
63             );
64             methord.Invoke("Five-ok");
65         }
66         /// <summary>
67         /// 方法只有一行去则掉大括号及分号
68         /// </summary>
69         public void DemoSix()
70         {
71             NoReturnWithParameters methord = new NoReturnWithParameters((o) => Console.WriteLine("有参无返回值:{0}", o));
72             methord.Invoke("Six-ok");
73         }
74         public void DemoSeven()
75         {
76             NoReturnWithParameters methord = (o) => Console.WriteLine("有参无返回值:{0}", o);
77             methord.Invoke("Seven-ok");
78         }
79         /// <summary>
80         /// 定义有参无返回值测试方法
81         /// </summary>
82         /// <param name="o"></param>
83         private void Test(string o)
84         {
85             Console.WriteLine("有参无返回值:{0}", o);
86         }
87         /*
88          * 作者:Jonins
89          * 出处:http://www.cnblogs.com/jonins/
90          */
91     }

 

 

  (1). delegate

同步委托&异步委托

同步委托:委托的Invoke方法用来进行同步调用。同步调用也可以叫阻塞调用,它将阻塞当前线程,然后执行调用,调用完毕后再继续向下进行。

异步委托:异步调用不阻塞线程,而是把调用塞到线程池中,程序主线程或UI线程可以继续执行。委托的异步调用通过BeginInvokeEndInvoke来实现。

以下为异步委托调用方式:

 1     class Program
 2     {
 3         /// <summary>
 4         /// 定义有参无返回值委托
 5         /// </summary>
 6         /// <param name="i"></param>
 7         public delegate void NoReturnWithParameters(string o);
 8         static void Main(string[] args)
 9         {
10             NoReturnWithParameters methord = new NoReturnWithParameters(Test);
11             Console.WriteLine("主线程执行1");
12             Console.WriteLine("主线程执行2");
13             methord.BeginInvoke("demo-ok", null, null);
14             Console.WriteLine("主线程执行3");
15             Console.WriteLine("主线程执行4");
16             Console.ReadKey();
17         }
18         /// <summary>
19         /// 异步调用委托方法
20         /// </summary>
21         /// <param name="o"></param>
22         static void Test(string o)
23         {
24             Console.WriteLine("有参无返回值:{0}", o);
25         }
26         /*
27          * 作者:Jonins
28          * 出处:http://www.cnblogs.com/jonins/
29          */
30     }

因为调用BeginInvoke为异步委托,不会阻塞主线程,运行结果如下:

图片 1

 

2.委托的定义

        delegate我们常用到的一种声明

异步回调(Callback)

异步回调通过设置回调函数,当调用结束时会自动调用回调函数,可以在回调函数里触发EndInvoke,这样就释放掉了线程,可以避免程序一直占用一个线程。

 1     class Program
 2     {
 3         /// <summary>
 4         /// 定义有参有返回值委托
 5         /// </summary>
 6         /// <param name="i"></param>
 7         public delegate string ReturnWithParameters(string o);
 8         static void Main(string[] args)
 9         {
10             ReturnWithParameters methord = new ReturnWithParameters(Test);
11             Console.WriteLine("主线程执行1");
12             Console.WriteLine("主线程执行2");
13             /*
14              BeginInvoke方法参数个数不确定, 最后两个参数含义固定,如果不使用的话,需要赋值null
15              委托的方法无参数,这种情况下BeginInvoke中只有两个参数。
16              此外,委托的方法有几个参数,BeginInvoke中从左开始,对应响应的参数。
17              1.倒数第二个参数:是有一个参数值无返回值的委托,它代表的含义为,该线程执行完毕后的回调。
18              2.倒数第一个参数:向即回调中传值,用AsyncState来接受。
19              3.其它参数:对应委托方法的参数。
20              */
21             IAsyncResult asyncResult = methord.BeginInvoke("demo-ok", new AsyncCallback(Callback), "AsycState:给回调函数的参数传递在此处出传值");
22             Console.WriteLine("主线程执行3");
23             Console.WriteLine("主线程执行4");
24             Console.ReadKey();
25         }
26         /// <summary>
27         /// 异步调用委托方法
28         /// </summary>
29         /// <param name="o"></param>
30         /// <returns></returns>
31         private static string Test(string o)
32         {
33             return "委托方法执行成功:"   o;
34         }
35         /// <summary>
36         /// 回调函数
37         /// </summary>
38         /// <param name="asyncResult"></param>
39         private static void Callback(IAsyncResult asyncResult)
40         {
41             /*
42              *asyncResult为回调前异步调用方法返回值
43              *AsyncResult 是IAsyncResult接口的一个实现类,引用空间:System.Runtime.Remoting.Messaging
44              *AsyncDelegate 属性可以强制转换为定义的委托类型
45              */
46             ReturnWithParameters methord = (ReturnWithParameters)((System.Runtime.Remoting.Messaging.AsyncResult)asyncResult).AsyncDelegate;
47             Console.WriteLine(methord.EndInvoke(asyncResult));
48             Console.WriteLine(asyncResult.AsyncState);
49         }
50         /*
51          * 作者:Jonins
52          * 出处:http://www.cnblogs.com/jonins/
53          */
54     }

执行结果如下:

图片 2

注意:

1.异步调用只能调用一次EndInvoke,否则会报错。

2.如果不回调函数中执行EndInvoke,请在异步调用后手动执行EndInvoke方法释放资源。

 

delegate int Add(int num1,int num2);

    Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型。

异步委托线程等待 

1.【Delegate】.EndInvoke(推荐)

1   public delegate void NoReturnWithParameters(string o);
2   NoReturnWithParameters noReturnWithParameters = new NoReturnWithParameters(...);
3        ......
4   noReturnWithParameters.EndInvoke(asyncResult);

2.【IAsyncResult】.AsyncWaitHandle.WaitOne(可以定义等待时间,超过等待时间不继续等待向下执行)

1  IAsyncResult asyncResult = null;
2  asyncResult.AsyncWaitHandle.WaitOne(2000);//等待2000毫秒,超时不等待

3.【IAsyncResult】.IsCompleted(是IAsyncResult对象的一个属性,该值指示异步操作是否已完成。不推荐)

1  IAsyncResult asyncResult = xxx.BeginInvoke(...);
2  while (!asyncResult.IsCompleted)
3  {
4      //正在等待中
5  }

 

delegate void ConvertNum(string result);

    例:public delegate int MethodtDelegate(int x, int y);表示有两个参数,并返回int型。

内置委托(泛化委托)

 .Net Framework 提供两个支持泛型的内置委托,分别是Action<>Func<>,在System命名空间中定义,结合lambda表达式,可以提高开发效率。

图片 3图片 4

使用方式如下:

 1     class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             //使用Action声明委托
 6             Action<string> action = TestAction;
 7             action.Invoke("action-demo-ok");
 8             //使用Func声明委托
 9             Func<string, string> func = TestFunc;
10             string result = func.Invoke("func-demo-ok");
11             Console.WriteLine(result);
12             Console.ReadKey();
13         }
14         private static void TestAction(string o)
15         {
16             Console.WriteLine("TestAction方法执行成功:{0}", o);
17         }
18         private static string TestFunc(string o)
19         {
20             return "TestFunc方法执行成功:"   o;
21         }
22         /*
23          * 作者:Jonins
24          * 出处:http://www.cnblogs.com/jonins/
25          */
26     }

Action:无返回值的泛型委托,目前.NET Framework提供了17个Action委托,它们从无参数到最多16个参数。

public delegate void Action

Action

无返回值的泛型委托

Action<int,string>

传入参数int、string,无返回值的委托

Action<int,string,bool> 

传入参数int,string,bool,无返回值的委托

Action<bool,bool,bool,bool> 

传入4个bool型参数,无返回值的委托

Action最少0个参数,最多16个参数,无返回值。

 

 

 

 

 

Func:有返回值的泛型委托,.NET Framework提供了17个Func函数,允许回调方法返回值。

public delegate TResult Func

Func<int> 

无参,返回值为int的委托

Func<int,string>

传入参数int,返回值为string类型的委托

Func<object,string,bool> 

传入参数为object, string 返回值为bool类型的委托

Func<T1,T2,,T3,int> 表示

传入参数为T1,T2,,T3(类型)返回值为int类型的委托

Func最少0个参数,最多16个参数,根据返回值泛型返回。必须有返回值,不可为void。

 

 

 

 

 

本质上ActionFunc都为delegate ,在System命名空间中定义(in和out用来标识变量)

图片 5

除此之外还有Predicate,它是固定返回值为bool类型的泛型委托。Action和Func足够使用这里不做介绍。

注意:

1.委托定义不要太多,微软仅在MSCorLib.dll中就有进50个委托类型,而且.NET Framework现在支持泛型,所以我们只需几个泛型委托(在System命名空间中定义)就能表示需要获取多达16个参数的方法。

2.如需获取16个以上参数,就必须定义自己的委托类型。所以建议尽量使用内置委托,而不是在代码中定义更多的委托类型,这样可以减少代码中的类型数量,同时简化编码。

3.如需使用ref或out关键字以传引用的方式传递参数,就需要定义自己的委托。

 

上面是定义两个委托的例子,其实很简单。声明一个委托使用delegate关键字,上面分别是定义的带返回值的委托和不带返回值的委托, 

  (2). Action

内置委托(泛化委托)参数协变&逆变

协变(out):假定S是B的子类,如果X(S)允许引用转换成X(B),那么称X为协变类。(支持“子类”向“父类”转换)
逆变(in):假定S是B的子类,如果X(B)允许引用转换成X(X),那么称X为协变类。(支持“父类”向“子类”转换)

正如泛化接口,泛型委托同样支持协变与逆变

1     public delegate void Action<in T>(T obj);
2    
3     public delegate TResult Func<out TResult>();

Action在System命名空间中定义支持逆变(in)

1         Action<object> x =...;
2         
3         Action<string> y = x;    

Func在System命名空间中定义支持协变(out)

1         Func<string> x =...;
2             
3         Func<object> y = x; 

如果要定义一个泛化委托类型,最好按照如下准则:
1.将只用在返回值的类型参数标注为协变(out)
2.将只用在参数的类型参数标注为逆变(in)

两个委托都有传递参数,当然也可以不传递参数。其实委托也是一个类,委托派生为System.MulticastDelegate,而System.MulticastDelegate

       Action是无返回值的泛型委托。

委托的兼容性

了解委托的兼容性,更易于在使用委托时使我们构建的代码具有多态性

1.类型的兼容性:即使签名相似,委托类也互不兼容。

1 delegate void D1();
2 delegate void D2();
3 ...
4 D1 d1=Method1;
5 D2 d2=d1;//编译时错误
6 D2 d2=new D2(d1);//这是允许的

如果委托实例执行相同的目标方法,则认为它们是等价的。

1 delegate void D();
2 ...
3 D1 d1=Method1;
4 D2 d2=Method1;
5 Console.WriteLine(d1==d2);//True

如果多播委托按照相同的顺序应用相同的方法责任委托它们是等价的。

2.参数的兼容性:当调用一个方法时,可以给方法的参数提供大于其指定类型的变量。这是正常的多态行为。同样,委托也可以又大于其目标方法参数类型的参数,即逆变。

 1     class Program
 2     {
 3         //委托接受string类型参数
 4         delegate void NoReturnWithParameters(string o);
 5         static void Main(string[] args)
 6         {
 7             NoReturnWithParameters noReturnWithParameters = new NoReturnWithParameters(Test);
 8             noReturnWithParameters("demo-ok");
 9             Console.ReadKey();
10         }
11         //目标方法接受object类型参数
12         static void Test(object o)
13         {
14             Console.WriteLine("返回值:{0}", o);
15         }
16     }

上述代码将参数string在调用目标方法时隐式向上转换为Object。

3.返回类型的兼容性:如果调用一个方法,得到的返回值类型可能大于请求的类型,这是正常多态行为。同样,委托的返回类型可以小于它的目标方法的返回值类型即协变**。**

 1     class Program
 2     {
 3         //委托返回object类型
 4         delegate object NoReturnWithParameters(string o);
 5         static void Main(string[] args)
 6         {
 7             NoReturnWithParameters noReturnWithParameters = new NoReturnWithParameters(Test);
 8             object o = noReturnWithParameters("demo-ok");
 9             Console.WriteLine(o);
10             Console.ReadKey();
11         }
12         //目标方法返回string类型
13         static string Test(string o)
14         {
15             return "返回值:"   o;
16         }
17     }

注意:标准事件模式的设计宗旨时再其使用公共基类EventArgs时应用逆变。例如,可以用两个不同的委托调用同一个方法,一个传递MouseEventArgs,另一个传递KeyEventArgs。

 

又继承System.Delegate,如果你知道这个也就明白委托其实是一个特殊的类。

   Action 表示无参,无返回值的委托

多播委托( =&-=)

所有的委托的实例都有多播的功能,自定义委托和内置委托都有,可以通过 =-=给委托增加和删掉不同的方法,当输入参数后,每个方法会按顺序进行迭代处理,并返回最后一个方法的计算结果。下面是简单模拟计算器的一段代码:

 1     class Program
 2     {
 3         public delegate int MulticastInstance(int inputA, int inputB);
 4         static void Main(string[] args)
 5         {
 6             MulticastInstance multicastInstance = Addition;
 7             multicastInstance  = new MulticastInstance(Reduce);
 8             multicastInstance  = new MulticastInstance(Multiply);
 9             int result = multicastInstance(10, 5);
10             Console.WriteLine("最后执行得到的结果为:{0}", result);
11             Console.ReadKey();
12         }
13         /// <summary>
14         /// 加法
15         /// </summary>
16         /// <param name="inputA"></param>
17         /// <param name="inputB"></param>
18         /// <returns></returns>
19         private static int Addition(int inputA, int inputB)
20         {
21             int result = inputA   inputB;
22             Console.WriteLine("Addition方法执行结果:{0}", result);
23             return result;
24         }
25         /// <summary>
26         /// 减法
27         /// </summary>
28         /// <param name="inputA"></param>
29         /// <param name="inputB"></param>
30         /// <returns></returns>
31         private static int Reduce(int inputA, int inputB)
32         {
33             int result = inputA - inputB;
34             Console.WriteLine("Reduce方法执行结果:{0}", result);
35             return result;
36         }
37         /// <summary>
38         /// 乘法
39         /// </summary>
40         /// <param name="inputA"></param>
41         /// <param name="inputB"></param>
42         /// <returns></returns>
43         private static int Multiply(int inputA, int inputB)
44         {
45             int result = inputA * inputB;
46             Console.WriteLine("Multiply方法执行结果:{0}", result);
47             return result;
48         }
49         /*
50          * 作者:Jonins
51          * 出处:http://www.cnblogs.com/jonins/
52          */
53     }

得到的结果如下:

图片 6

多播委托本质是:委托是不可变的,因此调用 =或-=的实质是创建一个新的委托实例,并把它赋值给已有变量。所有的委托类型都是从System.MulticastDelegate派生的,它又继承自System.Delegate,c#将委托中使用的 、-、 =、-=都编译成System.Delegate的静态CombineRemove方法。

 

 

   Action<int,string> 表示有传入参数int,string无返回值的委托

委托模拟观察者

能用委托解决的问题,都可以用接口解决。但再下面的情形中,委托可能是比接口更好的选择:

1.接口内之定义一个方法

2.需要多播能力

3.订阅者需要多次实现接口

下面代码是委托的观察者模式,优点是解耦且符合开放封闭原则:

 1 public class MulticastDelegates
 2 {
 3     public delegate int MulticastInstance(int inputA, int inputB);
 4     /// <summary>
 5     /// 模拟观察者
 6     /// </summary>
 7     public void Demo()
 8     {
 9         Manager manager = new Manager();
10         manager.Attach(new MulticastInstance(Add));
11         manager.Attach(new MulticastInstance(Reduce));
12         manager.Attach(new MulticastInstance(Multiply));
13         manager.Execute(10, 5);
14     }
15     /// <summary>
16     /// Observer模式、又称呼发布订阅或监听模式
17     /// </summary>
18     public class Manager
19     {
20         private MulticastInstance Handler;
21 
22         /// <summary>
23         /// 附加观察者
24         /// </summary>
25         /// <param name="handler1"></param>
26         public void Attach(MulticastInstance handler1)
27         {
28             Handler  = handler1;
29         }
30         /// <summary>
31         /// 分离观察者
32         /// </summary>
33         /// <param name="handler1"></param>
34         public void Detach(MulticastInstance handler1)
35         {
36             Handler -= handler1;
37         }
38         /// <summary>
39         /// 如果观察者数量大于0即执行播委托列表中的方法
40         /// </summary>
41         /// <param name="inputA"></param>
42         /// <param name="inputB"></param>
43         public void Execute(int inputA, int inputB)
44         {
45             if (Handler != null)
46                 if (Handler.GetInvocationList().Count() != 0)
47                     Handler(inputA, inputB);
48         }
49     }
50     private int Add(int inputA, int inputB)
51     {
52         int result = inputA   inputB;
53         Console.WriteLine("Add方法执行结果:{0}", result);
54         return result;
55     }
56     private int Reduce(int inputA, int inputB)
57     {
58         int result = inputA - inputB;
59         Console.WriteLine("Reduce方法执行结果:{0}", result);
60         return result;
61     }
62     private int Multiply(int inputA, int inputB)
63     {
64         int result = inputA * inputB;
65         Console.WriteLine("Multiply方法执行结果:{0}", result);
66         return result;
67     }
68 }

 

图片 7图片 8

   Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托

委托揭秘

委托看似很容易使用,通过delegate关键词定义,用熟悉的new构造委托实例,熟悉的方式调用回调函数,但实际上编译器和CLR在幕后做了大量工作来隐藏其复杂性。

重新审视上面计算器的一段代码:

1     public delegate int MulticastInstance(int inputA, int inputB);

事实上通过反编译可看到:

图片 9

编译器相当于定义了一个完整的类(继承自System.MulticastDelegate,定义四个方法:构造函数、Invoke、BeginInvoke和EndInvoke):

 

 1      internal class MulticastInstance : System.MulticastDelegate//继承System.MulticastDelegate
 2         {
 3             //构造器
 4             public MulticastInstance(object @object, IntPtr method);
 5             //这个方法的原型和源代码指定的一样
 6             public virtual int Invoke(int inputA, int inputB);
 7             //实现回调方法和异步回调
 8             public virtual IAsyncResult BeginInvoke(int inputA, int inputB, AsyncCallback callback, object @object);
 9             public virtual int EndInvoke(IAsyncResult result);
10         }
11         /*
12          * 作者:Jonins
13          * 出处:http://www.cnblogs.com/jonins/
14          */

所有委托类型都派生自System.MulticastDelegate类,System.MulticastDelegate派生自System.Delegate,后者又派生自System.Object。历史原因造成有两个委托类。
创建的所有委托类型豆浆MulticastDelegate作为基类,个别情况下仍会用到Delegate。Delegate类的两个静态方法CombineRemove的签名都指出要获取Delegate参数。由于创建的委托类型派生自MulticastDelegate,后者又派生自Delegate,所以委托类型的实例是可以传递给这两个方法的。

MulticastDelegate的三个重要非公共字段

字段 类型 说明
_target System.Object

当委托对象包装一个静态方法时,这个字段为null。当委托对象包装一个实例方法时,这个字段引用的是回调方法要操作的对象。

当委托对象包装一个实例方法时,这个字段引用的是回调方法要操作的对象。换言之

换言之,这个字段指出要传给实例方法的隐士参数的值。

_methodPtr System.IntPtr

一个内部的整数值,CLR用它标记要回调的方法。

_invocationList System.Object 该字段通常为null,构造委托链时它引用一个委托数组。

Delegate反编译后可看到静态方法CombineRemove(委托的 、-、 =、-=编译后的本质):

 1     [Serializable, ClassInterface(ClassInterfaceType.AutoDual), ComVisible(true), __DynamicallyInvokable]
 2     public abstract class Delegate : ICloneable, ISerializable
 3     {
 4         [ComVisible(true), __DynamicallyInvokable]
 5         public static Delegate Combine(params Delegate[] delegates);
 6         [__DynamicallyInvokable]
 7         public static Delegate Combine(Delegate a, Delegate b);
 8         [SecuritySafeCritical, __DynamicallyInvokable]
 9         public static Delegate Remove(Delegate source, Delegate value);
10     }

 

委托的简单实用例子Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public delegate string TeaDelegate(string spText);

    public class DelegateSource
    {
        public void TestDelegate()
        {
            Operator op = new Operator();
            TeaDelegate tea = new TeaDelegate(op.GetTea);
            Console.WriteLine("去给我倒杯水");
            Console.WriteLine();
            string result=tea("去给我倒杯水");
            Thread.Sleep(5000);
            Console.WriteLine(result);
            Console.WriteLine();
        }
    }

    public class Operator
    {
        /// <summary>
        /// 确定是否还有水
        /// </summary>
        private bool flag = true;

        public string GetTea(string spText)
        {
            if (spText == "去给我倒杯水")
            {
                if (flag)
                {
                    return "老公,茶来了";
                }
                else
                {
                    return "老公,没有水了";
                }
            }
            return "等待.......";
        }
    }

       Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托

 结语

同步委托将阻塞当前线程,等待方法执行完毕继续执行程序,相当于直接调用方法。异步委托是将方法放入线程池中执行并不阻塞主线程。异步委托从根本上说并不是多线程技术(任务Task也一样),就算异步委托内部将方法塞给线程池去执行也并不能说是开辟新线程执行方法,(线程池一定开辟新线程)这种说法并不严谨。委托本质是将调用者和目标方法动态关联起来,这是或许是我所理解的委托存在的最根本目的吧。

 

View Code

   Action至少0个参数,至多16个参数,无返回值。

参考文献

CLR via C#(第4版) Jeffrey Richter

C#高级编程(第7版) Christian Nagel

果壳中的C# C#5.0权威指南 Joseph Albahari

......


 

输出结果

   例:

图片 10 

        public void Test<T>(Action<T> action,T p)
        {
            action(p);
        }

上面使用最普通的一种方式来定义了一个委托的使用,这个例子虽然很简单,但是能够很形象的描述委托的使用。

    (3). Func

3.委托的三种形式

   Func是有返回值的泛型委托

(1).推断

   Func<int> 表示无参,返回值为int的委托

图片 11图片 12

   Func<object,string,int> 表示传入参数为object, string 返回值为int的委托

推断委托例子Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public delegate string TeaDelegate(string spText);

    public class DelegateSource
    {
        public void TestDelegate()
        {
            Operator op = new Operator();
            TeaDelegate tea = op.GetTea;
            Console.WriteLine("去给我倒杯水");
            Console.WriteLine();
            string result=tea("去给我倒杯水");
            Thread.Sleep(5000);
            Console.WriteLine(result);
            Console.WriteLine();
        }
    }

    public class Operator
    {
        /// <summary>
        /// 确定是否还有水
        /// </summary>
        private bool flag = true;

        public string GetTea(string spText)
        {
            if (spText == "去给我倒杯水")
            {
                if (flag)
                {
                    return "老公,茶来了";
                }
                else
                {
                    return "老公,没有水了";
                }
            }
            return "等待.......";
        }
    }

   Func<object,string,int> 表示传入参数为object, string 返回值为int的委托

View Code

   Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型)返回值为int的委托

在委托定义的例子中我们看到委托的使用方法是在委托实例化的时候指定的[new DelegateName(FunctionName)],这里可能表述不是太但是代码应该看得白了。 而委托的推断,并没有new 委托这个步骤,而是直接将Function 指定给委托。

   Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void

 (2).匿名函数

      例:   

图片 13图片 14

        public int Test<T1,T2>(Func<T1,T2,int>func,T1 a,T2 b)
        {
            return func(a, b);
        }
匿名委托例子Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public delegate string TeaDelegate(string spText);

    public class DelegateSource
    {
        public void TestDelegate()
        {
            Operator op = new Operator();
            bool flag = true;
            TeaDelegate tea = delegate(string spText)
            {
                if (spText == "去给我倒杯水")
                {
                    if (flag)
                    {
                        return "老公,茶来了";
                    }
                    else
                    {
                        return "老公,没有水了";
                    }
                }
                return "等待.......";
            };

            Console.WriteLine("去给我倒杯水");
            Console.WriteLine();
            string result=tea("去给我倒杯水");
            Thread.Sleep(5000);
            Console.WriteLine(result);
            Console.WriteLine();
        }
    }

    (4) .predicate

View Code

   predicate 是返回bool型的泛型委托

至于匿名委托,给人的感觉更为直接了,都不用显示的指定方法名,因为根本没有方法,而是指定的匿名方法。匿名方法在.NET 中提高了 

   predicate<int> 表示传入参数为int 返回bool的委托

代码的可读性和优雅性。对于更多操作较少的方法直接写为匿名函数,这样会大大提高代码的可读性。这里有两个值得注意的地方: 第一,不能使用

   Predicate有且只有一个参数,返回值固定为bool

跳转语句跳转到该匿名方法外,第二 不能使用ref,out修饰的参数

   例:public delegate bool Predicate<T> (T obj)

(3).多播委托

  

图片 15图片 16

  2.委托的使用

多播委托例子Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public delegate string TeaDelegate(string spText);

    public class DelegateSource
    {
        public void TestDelegate()
        {
            Operator op = new Operator();

            TeaDelegate tea1 = op.GetTea;
            TeaDelegate tea2 = op.Speak;
            TeaDelegate tea = tea1   tea2;

            Console.WriteLine("去给我倒杯水");
            Console.WriteLine();
            string result=tea("去给我倒杯水");
            Thread.Sleep(5000);
            Console.WriteLine(result);
            Console.WriteLine();
        }
    }

    public class Operator
    {
        /// <summary>
        /// 确定是否还有水
        /// </summary>
        private bool flag = true;

        public string GetTea(string spText)
        {
            if (spText == "去给我倒杯水")
            {
                if (flag)
                {
                    return "老公,茶来了";
                }
                else
                {
                    return "老公,没有水了";
                }
            }
            return "等待.......";
        }


        public string Speak(string spText)
        {
            Console.WriteLine("n去把我的设计图稿拿来");
            return null;
        }
    }

  (1).Delegate的使用  

View Code

public delegate int MethodDelegate(int x, int y);
        private static MethodDelegate method;
        static void Main(string[] args)
        {
            method = new MethodDelegate(Add);
            Console.WriteLine(method(10,20));
            Console.ReadKey();
        }

        private static int Add(int x, int y)
        {
            return x   y;
        }

还是上面的那个实例,我不尽想让女朋友去给我掉杯水,还让她帮我将程序设计图稿拿过来。这个时候做的就不是一件事了,而是多件。

(2).Action的使用

程序中也有很多这种情况,于是我们需要多播委托,在一个委托上指定多个执行方法,这是在程序中可以行的。上面提到了,委托直接继承于

static void Main(string[] args)
        {
            Test<string>(Action,"Hello World!");
            Test<int>(Action, 1000);
            Test<string>(p => { Console.WriteLine("{0}", p); }, "Hello World");//使用Lambda表达式定义委托
            Console.ReadKey();
        }
        public static void Test<T>(Action<T> action, T p)
        {
            action(p);
        }
        private static void Action(string s)
        {
            Console.WriteLine(s);
        }
        private static void Action(int s)
        {
            Console.WriteLine(s);
        }

System.MulticastDelegate,正是因为这个类可以实现多播委托。如果调用多播委托,就可以按顺序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果。所以在上面的这段代码中是得不到结果的

(3).Func的使用

 

static void Main(string[] args)
        {
            Console.WriteLine(Test<int,int>(Fun,100,200));
            Console.ReadKey();
        }
        public static int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
        {
            return func(a, b);
        }
        private static int Fun(int a, int b)
        {
            return a   b;
        }

4.事件

(4). predicate的使用

使用C#编程,无论是WinForm,WebForm 给人很难忘得就是它的控件,而他们的控件库使用方式都是使用使用事件驱动模式,而事件驱动模式却少不了委托。话不多说,看代码能够更清好的理解事件和委托之间的联系. 

 static void Main(string[] args)
        {
            Point[] points = { new Point(100, 200), 
            new Point(150, 250), new Point(250, 375), 
            new Point(275, 395), new Point(295, 450) };
            Point first = Array.Find(points, ProductGT10);
            Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
            Console.ReadKey();
        }
        private static bool ProductGT10(Point p)
        {
            if (p.X * p.Y > 100000)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

图片 17图片 18

使用带有 Array.Find 方法的 Predicate 委托搜索 Point 结构的数组。如果 X 和 Y 字段的乘积大于 100,000,此委托表示的方法 ProductGT10 将返回 true。Find 方法为数组的每个元素调用此委托,在符合测试条件的第一个点处停止。

事件的使用Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public delegate void MyDelegate(string name);

    public class EventSource
    {
        public event MyDelegate Event_Delegate;

        public void SetCustomer(string name)
        {
            Console.WriteLine("事件发生.....n");
            Console.WriteLine("hi! " name);
        }

        public void TestEvent()
        {
            EventSource source = new EventSource();
            Console.WriteLine("订阅事件.....n");
            source.Event_Delegate  = new MyDelegate(source.SetCustomer);
            Console.WriteLine("触发事件.....n");
            source.Event_Delegate("hechen");
            Console.WriteLine("..................");
        }
    }

  3.委托的清空

View Code

  (1).在类中申明清空委托方法,依次循环去除委托引用。

上面的代码中我们定义了一个委托,然后定义了一个类EventSource,这个类中声明了一个事件。定义一个事件使用event 关键字,定义一

         方法如下:

个event必须指定这个event传递消息的委托,在触发事件之前必需订阅事件,我们使用 = new 语法来订阅一个事件,也就相当于实例化一个事件。

 public MethodDelegate OnDelegate;                
        public void ClearDelegate()        
        {             
            while (this.OnDelegate != null) 
            {                 
                this.OnDelegate -= this.OnDelegate;  
            }        
        } 

当我们触发事件的时候,就会调用相应的方法去处理。

 (2).如果在类中没有申明清空委托的方法,我们可以利用GetInvocationList查询出委托引用,然后进行去除。  

 

  方法如下:

  1. 泛型委托
 public MethodDelegate OnDelegate; 
     static void Main(string[] args)
        {
            Program test = new Program();

            if (test.OnDelegate != null) 
            { 
                System.Delegate[] dels = test.OnDelegate.GetInvocationList(); 
                for (int i = 0; i < dels.Length; i  ) 
                {
                    test.OnDelegate -= dels[i] as MethodDelegate;
                }
            }
        }

委托是类型安全的引用,泛型委托就和我们常用的泛型类一样,这个类在使用的时候才能确定类型.通过泛型委托,我们可以在委托传递参数

4.委托的特点

之后知道它的类型.在.NET中有一个很典型的泛型委托:

  委托类似于 C 函数指针,但它们是类型安全的。
  委托允许将方法作为参数进行传递。
  委托可用于定义回调方法。
  委托可以链接在一起;例如,可以对一个事件调用多个方法。
  方法不必与委托签名完全匹配。

public delegate voie EventHandler<TEventArgs>(object sender,TEventArgs e) where TEventArgs:EventArgs.

  5.总结:

这是一个非常有特色的泛型委托,可能我们用的比较少,但是作用是不能忽视的。 我们看看三个非常具有代表性的泛型委托.现在.NET4.0已经出来了,但是泛型委托.NET2.0就出来了,Linq 大家用的那叫一个甜,

    Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型

为啥 函数式编程风格,匿名方法,Lamda表达式表达式使用是如此的魅力。但是大家仔细观察过没有,Linq 中的方法有几个经常出现的参数:

  Func可以接受0个至16个传入参数,必须具有返回值

Action<T>,Predicate<T>,Func<T, Result>

  Action可以接受0个至16个传入参数,无返回值

 Func<T, E>:封装一个具有一个参数并返回 E 参数指定的类型值的方法,T 是这个委托封装方法的参数类型,E是方法的返回值类型。当然Func<T, Result> 只是其中的一种情况,这个委托还有其他的几种情况:Func<T> 这个是方法没有参数,返回值类型是T;Func<T1,T2,Result> 这个方法有两个参数,类型分别为T1,T2,返回值是Result,还有Func<T1,T2,T3,Result>,Func<T1,T2,T3,T4,Result> 这几中情况,具体情况就不介绍了.我们还可以通过扩展类型,扩展为更多的参数.

  Predicate只能接受一个传入参数,返回值为bool类型

图片 19图片 20

 

Func 委托的使用Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public void TestFunc()
        { 
            TEventSource eventSource=new TEventSource();
            Func<string, string> func = eventSource.GetTea;
            string result = func("茶");
            Console.WriteLine(result);
        }

        public string GetTea(string context)
        {
            if (context == "茶")
            {
                return "茶来了";
            }
            else
            {
                return "设计稿子来了";
            }
        }

  详细参考:

View Code

       

Action<T>:封装一个方法,该方法只采用一个参数并且不返回值,包括Action<T>,Action<T1,T2>,Action<T1,T2,T3>,Action<T1,T2,T3,T4> 这几种情况,也可以通过扩展方法去扩展参数的个数 。

 

图片 21图片 22

 

Action 委托使用例子Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public void TestAction()
        {
            TEventSource eventSource = new TEventSource();
            Action<string> action = eventSource.Speak;
            action("Action<T> 泛型委托");
        }

        public void Speak(string context)
        {
            Console.WriteLine(context);
        }

 

Action 委托使用例子

Predicate<T>:表示定义一组条件并确定指定对象是否符合这些条件的方法。该委托返回的是一个bool类型的值,如果比较满足条件 

返回true,否则返回false.其实上面的Func 委托可以包含这个委托.不过这个委托和上面的两个不一样,它只有一种类型

图片 23图片 24

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 public void TestPredicate()
        {
            TEventSource eventSource = new TEventSource();
            Predicate<int> predicate = eventSource.IsRigth;
            Console.WriteLine(predicate(0));
        }

        public bool IsRigth(int value)
        {
            if (value == 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

Predicate 委托使用例子

6.异步委托

投票技术: 委托其实相当于一个线程,使用投票技术是使用异步委托的一种实现方式.Delegate类提供了方法BeginInvoke(),可以传送委托类型定义的输入参数,其返回类型为IAsyncResult。IAsyncResult的IsCompleted属性可以判断委托任务是否完成

图片 25图片 26

/// <summary>
    /// 使用投票操作完成委托任务
    /// </summary>
    public class VoteDelegate
    {
        /// <summary>
        /// 休眠特定时间执行操作
        /// </summary>
        /// <param name="data"></param>
        /// <param name="ms"></param>
        /// <returns></returns>
        public static int TakeWork(int data, int ms)
        {
            Console.WriteLine("开始调用TakeWork方法");
            Thread.Sleep(ms);
            Console.WriteLine("结束调用TakeWork方法");
            return data   10;
        }

        public void TestDelegate()
        {
            DelegateVote voteDel = TakeWork;
            IAsyncResult result = voteDel.BeginInvoke(1,5000,null,null);
            while (result.IsCompleted == false)
            {
                Console.WriteLine("等待......");
                Thread.Sleep(500);
            }
            int value = voteDel.EndInvoke(result);
            Console.WriteLine("委托调用结果:  " value);
        }
    }

异步委托投票技术

 

等待句柄:等待句柄是使用AsyncWaitHandle属性访问,返回一个WaitHandle类型的对象,它可以等待委托线程完成其任务。在这个参数中可以设置最大的等待时间。

图片 27图片 28

  public delegate string WaitDelegate(string content);

    public class WaitHandlerDelegate
    {
        public void TestWaitHander()
        {
            WaitDelegate del = GetTea;
            IAsyncResult ar = del.BeginInvoke("hechen", null, null);
            while (true)
            {
                Console.Write(".");
                if (ar.AsyncWaitHandle.WaitOne(50, false))
                {
                    break;
                }
            }
            string result=del.EndInvoke(ar);
            Console.WriteLine(result);

        }

        public static string GetTea(string content)
        {
            return "茶来了  " content;
        }
    }

异步委托等待句柄

异步回调:这个方式和投票技术有点类似,不过在投票方式中BeginInvoke()方法第三个参数指定了一个方法签名,而这个方法参数接收IAsyncResult 类型的参数。

图片 29图片 30

 public class AsyncresultDelegate
    {
        public void TestAsync()
        {
            AsyDelegate del = GetTea;
            del.BeginInvoke("hechen", delegate(IAsyncResult ar) {
                Thread.Sleep(5000);
                string result = del.EndInvoke(ar);
                Console.WriteLine(result);
            }, null);
            for (int i = 0; i < 100; i  )
            {
                Console.WriteLine("等待.....");
                Thread.Sleep(1000);
            }
        }

        public static string GetTea(string content)
        {
            return "茶来了  "   content;
        }
    }

异步委托回调函数

 

本文由星彩网app下载发布于计算机编程,转载请注明出处:寄托知识总括,委托的介绍

TAG标签: 星彩网app下载
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。