您当前的位置: 首页 >  c#

顺其自然~

暂无认证

  • 3浏览

    0关注

    1317博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

C# 委托 事件

顺其自然~ 发布时间:2020-07-24 08:20:25 ,浏览量:3

一:什么叫委托

通过反射发现,委托其实是一个类,继承自System.MulticastDelegate,但是System.MulticastDelegate这个类是特殊类,不能被继承

二:委托的声明

 public delegate void NoReturnNoParaOutClass();
 public class MyDelete
 {
      public delegate void NoReturnNoPara(T t);
      public delegate void NoReturnNoPara();
      public delegate void NoReturnWithPara(int x, int y);
      public delegate int WithReturnNoPara();
      public delegate string WithReturnWithPara(out int x, ref int y);
    }

委托可以声明在类外面,可以声明再类里面

三:委托的实例和调用

        private int GetSomething()
        {
            ;
        }
        private int GetSomething2()
        {
            ;
        }

        private int GetSomething3()
        {
            ;
        }
        private void DoNothing()
        {
            Console.WriteLine("This is DoNothing");
        }
        private static void DoNothingStatic()
        {
            Console.WriteLine("This is DoNothingStatic");
        }
        public string ParaReturn(out int x, ref int y)
        {
            throw new Exception();
        }
//多种途径实例化,要求传递一个参数类型,返回值都跟委托一致的方法
{
   WithReturnWithPara method = new WithReturnWithPara(ParaReturn);
   var dd = method.Invoke(out x, ref y);
 }
//begininvoke
{
   WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
   int iResult = method.Invoke();
   iResult = method();
   var result = method.BeginInvoke(null, null);//异步调用
   method.EndInvoke(result);
}
{
   NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);
   //委托实例的调用,参数和委托约束的一致
   method.Invoke(); //1
   //method(); //2
   //method.BeginInvoke(null, null); //3
   //this.DoNothing(); //1,2,3都等同于this.DoNothing
}
{
    NoReturnNoPara method = new NoReturnNoPara(DoNothingStatic);
}
{
    NoReturnNoPara method = new NoReturnNoPara(Student.StudyAdvanced);
}
{
    NoReturnNoPara method = new NoReturnNoPara(new Student().Study);
}

四:为什么要使用委托

有时候我们声明一个方法,直接调用蛮好的,为啥还要使用委托,然后还要先声明,再实例化,再inovke调用呢?

下面我们举个例子,比如一个人问好这件事情,不同人问候方式不一样,我们会先定义一个类型,如枚举

public enum PeopleType
 {
        Chinese,
        America,
        Japanese
 }

然后通过不同的类型来判断问候方式不同,如下

        /// 为不同的人,进行不同的问候
        /// 传递变量--判断一下----执行对应的逻辑
        /// 
        /// 
        /// 
        public void SayHi(string name, PeopleType peopleType)
        {
            switch (peopleType)
            {
                case PeopleType.Chinese:
                    Console.WriteLine($"{name}晚上好");
                    break;
                case PeopleType.America:
                    Console.WriteLine($"{name},good evening");
                    break;
                case PeopleType.Japanese:
                    Console.WriteLine($"{name},&&%*^^***@@@&&&&");
                    break;
                default:
                    throw new Exception("wrong peopleType"); //遇到异常报错
            }
        }

这样做的好处是:以后如果增加公共逻辑等比较容易,但是如果类型比较多,这个方法会变成无限制改动,导致方法难以维护,于是很多人想着增加分支,就增加方法--不影响别的方法的思路来改善

  public void SayHiChinese(string name)
  {
         Console.WriteLine($"{name}晚上好");
  }
  public void SayHiJapanese(string name)
  {
         Console.WriteLine($"{name},&&%*^^***@@@&&&&");
  }
  public void SayHiAmerican(string name)
  {
         Console.WriteLine($"{name},good evening");
  }

然后上层判断调用

这样做的好处是:修改某个方法--不影响别的方法 ,但是缺点却是:增加公共逻辑---多个方法就有很多重复代码

那么我们想:既增加逻辑方便,又维护简单,鱼肉熊掌,如何兼得呢?

我们可以把相应的逻辑做为参数传进来,这样就解决了我们的问题

具体我们可以按照以下来做:

  public void SayHiPerfact(string name, SayHiDeletegate method)
  {
      Console.WriteLine("增加开始日志");
      method.Invoke(name);
      Console.WriteLine("增加结束日志");
  }
  public delegate void SayHiDeletegate(string name);

然后调用的时候如下:

  SayHiDeletegate method = new SayHiDeletegate(SayHiChinese);

这样就做到了

1:逻辑解耦,方便维护

2:代码重构,去掉重复

其实这也是我们选择使用委托的两大优点

注意:以上我们纯粹为了定义委托而定义委托,其实框架已经我们帮我们定义了Action 和Func这两个委托,Action是没有返回值,Func是有返回值的,这两个委托类已经足够我们使用了,所以有时候我们使用的时候,没有必要自己再去定义,而直接使用即可

1:Action: 系统提供的,0-16个泛型参数,不带返回值的 委托

//Action 系统提供的,0-16个泛型参数,不带返回值的 委托
Action action0 = new Action(DoNothing);
Action action1 = this.DoNothing; //语法糖,就是编译器帮我们添加上new Action
Action action2 = this.ShowInt;

2:Func 系统提供的,0-16个泛型参数,带一个返回值的 委托

 //Func 系统提供的,0-16个泛型参数,带返回值的 委托
 Func func = this.GetSomething2;
 func.Invoke();

 Func func1 = this.ToString;
 func1.Invoke();

3:系统或者框架为什么要封装这样的方法?第一步我们定义一个这样的方法,需要传入一个Action

private void DoAction(Action act)
{
    act.Invoke();
}

然后我们可以这样调用:

Action action0 = this.DoNothing;
NoReturnNoPara method = this.DoNothing;
//   this.DoAction(method); //这样就不行,因为类型不一致
this.DoAction(action0);

但是我们不能使用 this.DoAction(method),因为:委托的本质是类,action和NoReturnNoPara是不同的类,虽然实例化都可以传递相同的方法,但是没有父子关系,所以不能替换,就像student和teacher两个类,实例化都是传递id/name,但是二者不能替换。

所以:框架提供这种封装,自然是希望大家都统一使用Action/Func,但是之前还有很多委托,至今再框架中还能看到,因为.net向前兼容,以前的版本去不掉的,保留着,这是历史包袱,此后我们就不要定义新的委托了!

五:多播委托

委托是一个类,然后继承于MulticastDelegate(多播委托),但是这个类是特殊类,所以任何一个委托都是多播委托类型的子类

 {
     //多播委托:任何一个委托都是多播委托类型的子类,可以通过+=去添加方法
     //多播委托如果中间出现未捕获的异常,方法链直接结束
     //多播委托:一个变量保存多个方法,可以增减;invoke的时候可以按顺序执行
     //+= 为委托实例按顺序增加方法,形成方法链,Invoke时,按顺序依次执行系列方法
     Student studentNew = new Student();

     NoReturnNoPara method = new NoReturnNoPara(this.DoNothing); //普通的DoNothing方法
     method += new NoReturnNoPara(this.DoNothing); //普通的DoNothing方法
     method += new NoReturnNoPara(DoNothingStatic); //静态方法
     method += new NoReturnNoPara(Student.StudyAdvanced); //静态方法
     method += new NoReturnNoPara(new Student().Study);//不是同一个实例,所以是不同的方法
     method += new NoReturnNoPara(studentNew.Study);
     method.Invoke();
     //method.BeginInvoke(null, null);//委托里面如果有多个Target,则不能异步调用,多播委托是不能异步的

     foreach (Action item in method.GetInvocationList()) //得到委托实例的target方法 ,静态方法target为null
     {
         item.Invoke();
         //item.BeginInvoke(null, null);
     }

     //-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除且只移除一个,没有也不异常
     method -= new NoReturnNoPara(this.DoNothing);
     method -= new NoReturnNoPara(DoNothingStatic);
     method -= new NoReturnNoPara(Student.StudyAdvanced);
     method -= new NoReturnNoPara(new Student().Study); //去不掉,原因是不同的实例的相同方法,并不吻合
     method -= new NoReturnNoPara(studentNew.Study);
     method.Invoke();
 }
 {
     WithReturnNoPara method = new WithReturnNoPara(this.GetSomething);
     method += new WithReturnNoPara(this.GetSomething2);
     method += new WithReturnNoPara(this.GetSomething3);
     int iResult = method.Invoke();//多播委托带返回值,结果以最后的为准,所以一般多播委托用的是不带返回值的
 }

注意:

+= 为委托实例按顺序增加方法,形成方法链,Invoke时,按顺序依次执行系列方法

-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合(new Student().Study如果声明两次则不属于吻合)的,移除且只移除一个,没有也不异常

多播委托其实就是观察者模式的缩影。

比如一只猫叫了一声,然后需要触发一系列的动作,比如老鼠跑,孩子哭,狗叫等等

如果我们把这些都写在猫的miao的方法中,如下:

public void Miao()
{
    Console.WriteLine("{0} Miao", this.GetType().Name);

    new Mouse().Run();
    new Baby().Cry();
    new Mother().Wispher();
    //new Brother().Turn();
    new Father().Roar();
    new Neighbor().Awake();
    new Stealer().Hide();
    new Dog().Wang();
}

上面的代码:

依赖太重,依赖多个类型,任何类型的变化都得修改猫职责耦合,猫不仅自己miao,还得找各种对象执行各种动作甚至控制顺序 任意环节增加或者减少调整顺序, 都得修改猫

那么我们可以修改代码,然后Cat的这类中,增加一个委托,然后猫只是自己叫,具体的其他的动作,只需要执行这个多播委托即可,代码如下:

 //猫 叫一声   触发一系列后续动作
 //多了个 指定动作  正是这个不稳定   封装出去   甩锅
 public MiaoDelegate MiaoDelegateHandler;
 public void MiaoNew()
 {
     Console.WriteLine("{0} MiaoNew", this.GetType().Name);
     if (this.MiaoDelegateHandler != null)
     {
         this.MiaoDelegateHandler.Invoke();
     }
 }

然后外面调用的时候可以直接通过下面:

 {
     Cat cat = new Cat();
     cat.MiaoDelegateHandler += new MiaoDelegate(new Mouse().Run);
     cat.MiaoDelegateHandler += new MiaoDelegate(new Baby().Cry);
     cat.MiaoDelegateHandler += new MiaoDelegate(new Mother().Wispher);
     cat.MiaoDelegateHandler += new MiaoDelegate(new Brother().Turn);
     cat.MiaoDelegateHandler += new MiaoDelegate(new Father().Roar);
     cat.MiaoDelegateHandler += new MiaoDelegate(new Neighbor().Awake);
     cat.MiaoDelegateHandler += new MiaoDelegate(new Stealer().Hide);
     cat.MiaoDelegateHandler += new MiaoDelegate(new Dog().Wang);
     cat.MiaoNew();//调用
     Console.WriteLine("***************************");
 }

这样猫就减少了依赖,而且猫叫后,各种动作的顺序也可以随便改变,而不会改变猫叫的方法!

六:事件

事件:是带event关键字的委托的实例,event可以限制变量被外部调用/直接赋值event:限制权限,只允许在事件声明类里面去invoke和赋值,不允许外面,甚至子类都能运用

event事件只能声明在类中,而委托可以声明在类外面

比如我们在cat类中定义一个事件

 public event MiaoDelegate MiaoDelegateHandlerEvent;
 public void MiaoNewEvent()
 {
     Console.WriteLine("{0} MiaoNewEvent", this.GetType().Name);
     if (this.MiaoDelegateHandlerEvent != null)
     {
         this.MiaoDelegateHandlerEvent.Invoke();
     }
 }

然后只能在cat类中中进行invoke调用,如果我们定义一个子类继承cat类,如下:

public class ChildClass : Cat
{
    public void Show()
    {
        this.MiaoDelegateHandlerEvent += null;
        if (this.MiaoDelegateHandlerEvent != null)//子类也不能调用,调用报错
        {
            this.MiaoDelegateHandlerEvent.Invoke();
        }
    }
}
this.MiaoDelegateHandlerEvent.Invoke(); 这样是不能使用的,Invoke这个是完全不能调用的!

委托和事件的区别与联系?委托是一个类型,比如Student事件是委托类型的一个实例,加上了event的权限控制 比如学生加菲猫,是一个确切的实体

然后事件也可以通过下面来调用多个方法

{
    Cat cat = new Cat();
    //cat.MiaoDelegateHandler += new MiaoDelegate(new Mouse().Run);
    cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Baby().Cry);
    cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Mother().Wispher);
    cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Brother().Turn);
    cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Father().Roar);
    cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Neighbor().Awake);
    cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Stealer().Hide);
    cat.MiaoDelegateHandlerEvent += new MiaoDelegate(new Dog().Wang);
    cat.MiaoNewEvent();
    Console.WriteLine("***************************");
}

下面写一个标准的的事件,事件一般分为三种:

1:事件的发布者:发布事件,并且在满足条件时候触发事件

2:事件的订户:关注事件,事件发生后,自己做出相应的动作

3:事件的订阅:把订户和发布者的事件关联起来

     /// 
     /// 委托是一种类型,静态类不能被继承,所以委托不能声明静态,
     /// event只是一个实例,所以可以生成实例
     /// 
     class EventStandard
     {
         /// 
         /// 订阅:把订户和发布者的事件关联起来
         /// 
         public static void Show()
         {
             iPhoneX phone = new iPhoneX()
             {
                 Id = ,
                 Tag = "1.0"
             };
             // 订阅:把订户和发布者的事件关联起来
             phone.DiscountHandler += new Student().Buy;
             phone.DiscountHandler += new Teacher().Notice;

             phone.Price = ;

         }
         /// 
         /// 订户:关注事件,事件发生后,自己做出对应的动作
         /// 
         public class Student
         {
             public void Buy(object sender, EventArgs e)
             {
                 iPhoneX phone = (iPhoneX)sender;
                 Console.WriteLine($"this is {phone.Tag} iphoneX");
                 XEventArgs args = (XEventArgs)e;
                 Console.WriteLine($"之前的价格{args.OldPrice}");
                 Console.WriteLine($"限制的价格{args.NewPrice}");
                 Console.WriteLine("立马买!!");
             }
         }
         public class Teacher
         {
             public void Notice(object sender, EventArgs e)
             {
                 iPhoneX phone = (iPhoneX)sender;
                 Console.WriteLine($"this is {phone.Tag} iphoneX");
                 XEventArgs args = (XEventArgs)e;
                 Console.WriteLine($"之前的价格{args.OldPrice}");
                 Console.WriteLine($"限制的价格{args.NewPrice}");
                 Console.WriteLine("立马买!!");
             }
         }

         /// 
         /// 事件参数 一般会为特定的事件去封装个参数
         ///订户:Teacher/Student:关注事件,事件发生后,自己做出对应的动作
         /// 
         public class XEventArgs : EventArgs
         {
             public int OldPrice { set; get; }
             public int NewPrice { set; get; }
         }

         /// 
         /// 事件的发布者,发布事件,并且在满足条件时候触发事件
         /// 
         public class iPhoneX
         {
             public int Id { set; get; }
             public string Tag { set; get; }
             public int Price
             {
                 set
                 {
                     if (value < this._price)
                     {
                         this.DiscountHandler?.Invoke(this, new XEventArgs() { OldPrice = this._price, NewPrice = value });
                         this._price = value;
                     }
                 }
                 get { return this._price; }
             }

             private int _price;

             /// 
             /// 打折事件
             ///
             /// 
             public event EventHandler DiscountHandler;
         }
     }

EventHandler:框架自带,表示将用于处理不具有事件数据的事件的方法。EventHandler(object sender, EventArgs e)

下面这个例子也很经典

   /// 
     /// 妈妈做好饭,触发爸爸和儿子一起吃饭,并且妈妈会把对应的菜单同时告知爸爸和儿子
     /// 
     public class MyEvenStandard
     {
         public class Meal
         {
             public static void FinishMeal()
             {
                 Mom mom = new Mom();
                 mom.EatHandler += (new Dad()).Eat;
                 mom.EatHandler += (new Son()).Eat;
                 mom.Cook();
             }
         }

         public class Mom
         {
             //EventHandler(object sender, EventArgs e) 事件的原型,里面sender则是发布者自己,e:是发布者的交互参数
             public event EventHandler EatHandler;
             public void Cook()
             {
                 Console.WriteLine("开始吃饭了");
                 EatHandler?.Invoke(this, new MenuArags() { Fruid = "orange", Greens = "green pepper", Meat = "fish" });
             }
         }
         /// 
         /// 爸爸
         /// 
         public class Dad
         {
             public void Eat(object sender, EventArgs args)
             {
                 var menArgs = (MenuArags)args;
                 Console.WriteLine($"儿子,我们今天晚上次:{menArgs.Greens},{menArgs.Fruid},{menArgs.Meat}");
             }
         }

         /// 
         /// 儿子
         /// 
         public class Son
         {
             public void Eat(object sender, EventArgs args)
             {
                 var menArgs = (MenuArags)args;
                 Console.WriteLine($"爸爸,我们今天晚上次:{menArgs.Greens},{menArgs.Fruid},{menArgs.Meat}");
             }
         }

         public class MenuArags : EventArgs
         {
             /// 
             /// 水果
             /// 
             public string Fruid { set; get; }

             /// 
             /// 青菜
             /// 
             public string Greens { set; get; }

             public string Meat { set; get; }
         }

     }
关注
打赏
1662339380
查看更多评论
立即登录/注册

微信扫码登录

0.1254s