关于委托的研究

本文详细介绍了C#委托的概念、使用场景、优势、如何使用以及如何指向多个方法和处理带返回值的方法。通过实例展示了委托在多线程、事件处理等场景的应用,以及在类内部使用委托的方法。此外,还探讨了委托与事件的区别,以及如何在类内部使用事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近有朋友因为委托的问题一直受困扰,这里总结一下

什么是委托?

  委托顾名思义,就是自己不干,让别人干,这个有别与C和C++ 的函数指针(还是学过C和C++的),这里是一种方法,而且不管是静态方法还是非静态方法,而函数指针只是指向静态函数,这点也是两者的主要区别。一般委托会用在观察者模式(Observer)里。

为什么要用?什么时候用委托?或者说使用委托的好处是什么呢?

   说白了委托就是第三方,调用者告诉第三方要做什么,然后调用者就不用管了,这个委托(第三方)就会去调用方法去帮你实现。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C++中的函数指针不同,委托是面向对象,而且是类型安全的。

好处:

1、相当于用方法作为另一方法参数(类似于C的函数指针)
2、在两个不能直接调用的方法中作为桥梁,如:在多线程中的跨线程的方法调用就得用委托
3、当不知道方法具体实现什么时使用委托,如:事件中使用委托

那么如何使用委托呢?这里总结了一下,主要分三步走;

一、定义一个委托delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。 

二、创建delegate对象,并将你想要的传递函数作为参数传入。

创建代理对象的方法:(区别是在于方法是否静态)

1、MyDelegate myDeleGate=new MyDelegate(实例名.方法名)

2、MyDelegate myDeleGate=new MyDelegate(类名.方法名)

三、调用,上面定义的对象调用方法。

如何使用委托呢?也是有三种方式;

一、Labmda表达式

  List<int > list = new List<int> ();//定义一个list数组
            list.Add (121);//添加数据
            list .Add (234);
            list .Add (123);
            list .Add (123);
            list .Add (123);
            foreach (var i in list) {//遍历打印
                Console.Write(i+" ");
            }
            Console.WriteLine ();
            list .RemoveAll ((int x) => {//定义委托指向删除方法<pre name="code" class="csharp">  if (x == 123 || x == 234) {
                    return true;
                }
                return false;
            });
            foreach (var i in list) {//遍历打印删除方法执行后的数组
                Console.Write(i+" ");
            }<span style="font-family: Arial, Helvetica, sans-serif;">         </span>
 

二、匿名委托(通俗讲是直接加方法体)

list.RemoveAll (delegate (int x)
           {
               if (x == 123||x==234) {
                   return true;<pre name="code" class="csharp">                            }
                return false;
                  });
           foreach (var i in list) {
               Console.Write(i+" ");
            }<span style="font-family: Arial, Helvetica, sans-serif;">      </span>
 
三、就是比较常见的也是使用最多的 

using System.Collections;
using System.Collections.Generic;

namespace lesson13
{
     delegate void myDelegate(double money);//1、定义一个参数的委托
    class Host{//定义Host的类
        public  void sell(double money)
             {
                  Console.WriteLine (money+"sell");
                       }
    }

    class MainClass
    {
        public static void Main (string[] args)
        {
                     
            Host h = new Host ();//实例化
            myDelegate my = h.sell;//2、定义委托对象指向方法         
            my(14.5);3、调用  也可以使用 my.Invoke(14.5);
      }
显然运行结果就是14.5sell。调用委托对象的Invoke()方法,Invoke后面的括号中应包含调用该委托中的方法时使用的参数。实际上,给委托实例提供括号与调用委托类的Invoke()方法完全相同。因为Invoke()方法是委托的同步调用方法。

如何指向多个方法呢?

因为委托就是委托与相同类型方法,比如游戏里的回血回蓝啊,这样的就需要委托多个方法。

一个委托可以通过+运算委托关联多个方法,叫多路广播委托(对应于单路广播委托)。

namespace ConsoleApplication1
{
       delegate void myDelegate(double money);
    class Host{
        public  void rent(double money)
          {
                  Console.WriteLine (money+"rent");
             }
        public  void sell(double money)
             {
                  Console.WriteLine (money+"sell");
                       }
                }

    class MainClass
    {
        public static void Main (string[] args)
        {
                     
            Host h = new Host ();
            myDelegate my = h.sell;            
            my += h.rent;
            my (12.2);
显然结果就是12.2sell

                       12.2rent

            要是删除这个委托方法,

            my -= h.rent;
显然结果就是12.2sell了。

如果方法有返回值呢?情况是不一样的。

namespace ConsoleApplication1
{
       delegate int SumDelegate(int x,int y);//1、定义两个参数的委托
    class Host{
        public int sum(int a,int b)
            {
                   return a + b;
                     }
        public int sum1(int a,int b)
             {
                    return a + b+5;
                        }  
    }
    class MainClass
    {
        public static void Main (string[] args)
        {
                     
            Host h = new Host ();//实例化
            SumDelegate sum = h.sum;
            sum += h.sum1;
            Console.WriteLine (sum(2,3));//带返回值的委托返回最后一个
             }}
结果带返回值的类型在使用多路广播的时候只是打印了10=2+3+5,也就是说只是委托了最后一个

上面的委托定义在类外面,委托写类里这种情况跟上面的情况一样么?是有区别的

委托写在类里,就是私有的;外面拿不到。要想用就用event

class Fun{
        //委托写在类里,就是私有的;外面拿不到。要想用就用event
          public   delegate void Greet(string str);
          public  event Greet greet;
        public void s(string name) {
            greet (name);
        }
    }<pre name="code" class="csharp">  public static void Main (string[] args)
        {
                     
            Fun fun= new Fun ();
            fun.greet += delegate (string name) {
                Console.WriteLine ("爱" + name);
            };
            fun.s("老七");}//<pre name="code" class="csharp">结果:爱老七;给委托添加了一个打印的方法,传个值“老七”,同时也是实现了方法,就打印了。
 

这里可以看出用法其实只是加个Event其他用法还是可以一样使用的。可是我们使用的是greet不是Greet.

上面还定义一个方法  :

public void s1(string name)
             {
                 Console.WriteLine(name + "老七");
                       }<pre name="code" class="csharp">public static void Main (string[] args)
        {                
            Fun fun= new Fun (); 
            fun.greet += fun.s1;
            fun.s1("爱");}
 

输出一样的结果。

这里必须使用+=运算符。

这里还有一个问题,我们把Event删了还是可以执行的,为什么呢?不是说写在类了必须要用Event么?

这里是以设计模式——观察者模式来说;

      event是对委托对象的一个封装,不用event直接就可以用了,相当于一个公有的变量直接在类外面用了.破坏了封装,如果使用event,直接不能用,只能调用方法来用,这样就没有破坏封装了.

      event 事件都可以用delegate来完成。event强调了事件,通过订阅事件来获得通知。

      事件是针对某一个具体的对象的,一般在该对象的所属类A中写好事件,并且写好触发事件的方法,那么这个类A就是事件的发布者,然后在别的类B里面定义A的对象,并去初始化该对象的事件,让事件指向B类中的某一个具体的方法,B类就是A类事件的订阅者。当通过A类的对象来触发A类的事件的时候(只能A类的对象来触发A类的事件,别的类的对象不能触发A类的事件,只能订阅A类的事件,即实例化A类的事件),作为订阅者的B类会接收A类触发的事件,从而使得订阅函数被执行。一个发布者可以有多个订阅者,当发布者发送事件的时候,所有的订阅者都将接收到事件,从而执行订阅函数,但是即使是有多个订阅者也是单线程



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值