四、委托及事件学习笔记

基本知识点:委托的声明、实例化、调用方法

1.委托的声明
委托既可以声明在类的外边,也可以声明在类的内部,委托与抽象方法很像,有返回值也有参数,用一个delegate关键字修饰
2.通过IIL反编译可以看到委托就是一个类,继承自MulticastDelegate(多播委托),MulticastDelegate是一个特殊的类,我们无法继承
3.观察VS编辑器,类名和委托名同一个颜色,而方法名却是另一个颜色,这也佐证了委托其实就是一个类

public delegate void NoReturnNoParaOutClass();
public class CustomDelegate
{
    public delegate void NoReturnNoPara();
    public delegate void NoReturnWithPara(int x, int y);
    public delegate string WithReturnNoPara();
    public delegate string WithReturnWithPara(int x, int y);

    public void ShowBasicInfo()
    {
        Console.WriteLine();
        Console.WriteLine("------------------基本知识点:委托的声明、实例化、调用方法-------------------");
        Console.WriteLine();
        //委托的实例化:委托可以实例化,需要传递一个方法进来,但是必须严格要求传递一个委托参数、返回值完全一致的方法
        NoReturnNoParaOutClass noReturnNoParaOutClass = new NoReturnNoParaOutClass(new AllMethods().NoReturnNoParaMethod);
        noReturnNoParaOutClass.Invoke();
        Console.WriteLine();

        NoReturnNoPara noReturnNoPara = new NoReturnNoPara(new AllMethods().NoReturnNoParaMethod);
        noReturnNoPara.Invoke();//执行这个委托,就把当前传递给委托的这个方法执行一下
        //noReturnNoPara.BeginInvoke(null, null);//这里是开启一个新的线程去执行方法
        //但是在.NetCore中不支持Action.BeginInvoke的委托异步调用方法
        //noReturnNoPara.EndInvoke();//需要method.BeginInvoke的执行结果作为参数
        Console.WriteLine();

        //在实例化的时候,要求传递进来的方法结构和委托完全一致,要求参数和返回值保持一致
        NoReturnWithPara noReturnWithPara = new NoReturnWithPara(new AllMethods().NoReturnWithParaMethod);
        noReturnWithPara.Invoke(1, 2);
        Console.WriteLine();

        WithReturnNoPara withReturnNoPara = new WithReturnNoPara(new AllMethods().WithReturnNoParaMethod);
        string noParaResult = withReturnNoPara.Invoke();
        Console.WriteLine(noParaResult);
        Console.WriteLine();

        WithReturnWithPara withReturnWithPara = new WithReturnWithPara(new AllMethods().WithReturnWithParaMethod);
        string withParaResult = withReturnWithPara.Invoke(251, 357);
        Console.WriteLine(withParaResult);

        Console.WriteLine("委托的本质是什么?");
        Console.WriteLine("委托的本质其实就是一个类,继承自MulticastDelegate,MulticastDelegate是一个特殊的类,我们无法继承这个类");
    }
}

基本知识点:委托的声明、实例化、调用方法

基本知识点:委托的多种实例化方法

public void ShowInstantiation()
{
    Console.WriteLine();
    Console.WriteLine("--------------基本知识点:委托的多种实例化方法--------------");
    Console.WriteLine();

    NoReturnNoPara noreturnnopara1 = new NoReturnNoPara(new AllMethods().NoReturnNoParaMethod);
    noreturnnopara1.Invoke();
    Console.WriteLine();

    Student student = new Student()
    {
        Id = 123,
        Name = "渡边",
        ClassId = 2,
        Age = 23
    };
    NoReturnNoPara noreturnnopara2 = new NoReturnNoPara(student.Study);
    noreturnnopara2.Invoke();
    Console.WriteLine();

    NoReturnNoPara noreturnnopara3 = new NoReturnNoPara(Student.StudyAdvanced);
    noreturnnopara3.Invoke();
    Console.WriteLine();

    NoReturnNoPara noreturnnopara4 = new NoReturnNoPara(AllMethods.DoNothingStatic);
    noreturnnopara4.Invoke();
    Console.WriteLine();
}
public class Student
{
    public int Id { get; set; }

    public string Name { get; set; }

    public int ClassId { get; set; }

    public int Age { get; set; }

    public void Study()
    {
        Console.WriteLine($"{Name}正在学习.net高级课");
    }

    public static void StudyAdvanced()
    {
        Console.WriteLine($"学习.net高级班vip课");
    }
}

基本知识点:委托的多种实例化方法

基本知识点:多播委托

其实我们上面声明的任何一个委托都是多播委托
多播委托可以在实例化以后,通过+=添加和当前委托的参数、返回值类型完全一致的多个方法,形成方法链,在调用Invoke方法的时候,会按照+=的顺序依次执行各个方法
多播委托还可以通过-=移除方法,移除的必须是同一个方法,是按照从下往上的顺序依次匹配,只要匹配到一个就移除一个,而且只能移除一个,如果匹配不到,就啥也不干,也不会报错。
问题
对于方法名一样但参数不一样(即重载方法)使用委托会存在问题吗?
不会有问题的,因为声明委托的时候已经规定了委托的参数和返回值类型,而我们通过委托添加的方法,委托和返回值必须一致,因此不会存在问题。

public void ShowMulticastDelegate()
{
    Console.WriteLine();
    Console.WriteLine("-----------基本知识点:多播委托------------");
    Console.WriteLine();
    Student student = new Student()
    {
        Id = 10,
        Name = "渡边",
        ClassId = 1,
        Age = 20
    };

    NoReturnNoPara noreturnnopara = new NoReturnNoPara(new AllMethods().NoReturnNoParaMethod);
    noreturnnopara += new AllMethods().NoReturnNoParaMethod;
    noreturnnopara += AllMethods.DoNothingStatic;
    noreturnnopara += Student.StudyAdvanced;
    noreturnnopara += new Student()
    {
        Id = 15,
        Name = "渡边",
        ClassId = 1,
        Age = 20
    }.Study;
    noreturnnopara += student.Study;
    //Lambda表达式在中间语言IL中还是生成不同的两个方法,生成的时候其实会有两个不同的名字
    noreturnnopara += () => { Console.WriteLine("执行Lambda表达式"); };
    
    noreturnnopara -= new AllMethods().NoReturnNoParaMethod;
    noreturnnopara -= AllMethods.DoNothingStatic;
    noreturnnopara -= Student.StudyAdvanced;
    noreturnnopara -= new Student()
    {
        Id = 15,
        Name = "渡边",
        ClassId = 1,
        Age = 20
    }.Study;//因为这个Student对象跟上边的Student对象不是一个了
    noreturnnopara -= student.Study;
    noreturnnopara -= () => { Console.WriteLine("执行Lambda表达式"); };

    noreturnnopara.Invoke();
    //noreturnnopara.BeginInvoke(null, null);//开启一个新的线程去完成计算,多播委托不允许异步
    Console.WriteLine();
    foreach(NoReturnNoPara item in noreturnnopara.GetInvocationList())
    {
        //item.BeginInvoke(null, null);//这样执行异步操作,但是.NetCore不支持BeginInvoke?
        //报错:System.PlatformNotSupportedException:“Operation is not supported on this platform.”
        item.Invoke();
    }
}

基本知识点:多播委托

基本知识点:Action和Func

Action的本质就是系统定义的一个委托,是一个“没有返回值”、“可以有参数”的委托
Action的参数最多支持16个,如果超出16个,可以自定义Action的参数
Func可以有返回值,也可以带参数

Func的参数最多可以有16+1个,前16个是参数类型,后边1个是返回值类型,如果需要17个及以上的参数,可以自己定义

为什么框架要给我们提供这两个委托呢?
既然是框架提供的委托,自然是希望我们就是用这个委托
以下两者,都需要带一个参数但没有返回值的委托

new Thread(new ParameterizedThreadStart(new AllMethods().DoObject));
ThreadPool.QueueUserWorkItem(new WaitCallback(new AllMethods().DoObject));
new Thread(new WaitCallback(new AllMethods().DoObject));

同样是需要一个object参数且没有返回值的委托,为什么上边会报错?
因为委托的本质其实是一个类,定义的不同委托其实是不同的类,这些类没有父子级别关系,不能替换
后边在使用委托的时候,就不要自己定义了,直接使用Action、Func,这两个基本就够满足所有委托的需求了,使用Action、Func其实也是为了避免在开发工作中过多地定义委托,其实是个冗余
问题
那为什么还可以自己定义委托呢?
因为历史原因,在以前的框架中使用了各种不同的委托,C#是向前兼容的,这是历史包袱

public void ShowActionFunc()
{
    Console.WriteLine();
    Console.WriteLine("--------------基本知识点:Action和Func---------------");
    Console.WriteLine();

    Student student = new Student()
    {
        Id = 10,
        Name = "绿子",
        ClassId = 1,
        Age = 20
    };
    Console.WriteLine("Action的本质就是系统定义的一个委托,是一个“没有返回值”、可以有参数的委托");
    Console.WriteLine();

    Action action = new Action(new AllMethods().NoReturnNoParaMethod);
    action += new AllMethods().NoReturnNoParaMethod;
    action += new Student()
    {
        Id = 12,
        Name = "渡边",
        ClassId = 2,
        Age = 21
    }.Study;
    action += student.Study;
    action += Student.StudyAdvanced;
    action += AllMethods.DoNothingStatic;
    action += () => { Console.WriteLine("执行了Lambda表达式"); };

    action -= new AllMethods().NoReturnNoParaMethod;
    action -= new Student()
    {
        Id = 12,
        Name = "渡边",
        ClassId = 2,
        Age = 21
    }.Study;
    action -= student.Study;
    action -= Student.StudyAdvanced;
    action -= AllMethods.DoNothingStatic;
    action -= () => { Console.WriteLine("执行了Lambda表达式"); };
    action.Invoke();
    Console.WriteLine();

    Action<int, int> action1 = new Action<int, int>(new AllMethods().NoReturnWithParaMethod);
    action1.Invoke(12523, 9463);
    Console.WriteLine();

    Action<int, string, DateTime> action2 = new Action<int, string, DateTime>(new AllMethods().DoNothingComplex);
    action2.Invoke(8323, "渡边", DateTime.Now);
    Console.WriteLine();

    //Action的参数最多支持16个,如果超出16个,可以自定义Action的参数
    Action<int, string, DateTime> action3 = new AllMethods().DoNothingComplex;
    action3.Invoke(24924, "直子", DateTime.Now);
    Console.WriteLine();

    //Func可以有返回值,也可以带参数
    Func<int, int, string> func1 = new Func<int, int, string>(new AllMethods().WithReturnWithParaMethod);
    func1.Invoke(432, 983);
    Console.WriteLine();
    //第一个参数是参数类型,第二个参数是返回值类型
    //Func<int, string> func2 = new Func<int, string>(this.DoNothingToString);
}

基本知识点:Action和Func

简单的“俄罗斯套娃”

public class CustomDelegateExtendsion
 {
     public void ShowSimpleDools()
     {
         InvokerAction invoker = new InvokerAction();

         Type type = invoker.GetType();
         MethodInfo method = type.GetMethod("Method");
         //已经通过反射找到了Method方法,即要执行的核心业务逻辑

         //以下仅仅是包装了一个委托,需要通过action.Invoke()才会真的执行对应的方法
         Action action = new Action(() =>
         {
             method.Invoke(invoker, null);
         });

         if (method.IsDefined(typeof(BeforeMethodAttribute), true))
         {
             BeforeMethodAttribute attribute = method.GetCustomAttribute<BeforeMethodAttribute>();
             Action actionResult = attribute.Do(action);
             actionResult.Invoke();
         }
         Console.WriteLine();
         if (method.IsDefined(typeof(BeforeMethodLogAttribute), true))
         {
             BeforeMethodLogAttribute attribute = method.GetCustomAttribute<BeforeMethodLogAttribute>();
             Action actionResult = attribute.Do(action);
             actionResult.Invoke();
         }
         Console.WriteLine("------------------------------------");
         foreach(AbstractAttribute attribute in method.GetCustomAttributes())
         {
             Action actionResult = attribute.Do(action);
             actionResult.Invoke();
             Console.WriteLine();
         }
     }
 }

 public class InvokerAction
 {
     /// <summary>
     /// 核心业务逻辑
     /// 如果想在此之前做点什么事情,而且希望前面做的事情能够自由装配
     /// </summary>
     [BeforeMethod]
     [BeforeMethodLog]
     public void Method()
     {
         Console.WriteLine("这里是要执行的核心业务逻辑");
     }
 }

 [AttributeUsage(AttributeTargets.Method,AllowMultiple = true,Inherited = true)]
 public abstract class AbstractAttribute : Attribute
 {
     public abstract Action Do(Action action);
 }

 public class BeforeMethodAttribute : AbstractAttribute
 {
     public override Action Do(Action action)
     {
         Action actionResult = new Action(() =>
         {
             Console.WriteLine("在核心业务执行之前执行的业务逻辑");//这里才是核心业务逻辑前增强的业务逻辑,可以随便写
             action.Invoke();
         });
         
         return actionResult;
     }
 }

 public class BeforeMethodLogAttribute : AbstractAttribute
 {
     public override Action Do(Action action)
     {
         Action actionResult = new Action(() =>
         {
             Console.WriteLine("在核心业务逻辑前打印日志");
             action.Invoke();
         });
         return actionResult;
     }
 }

多播委托的应用

public interface IObject
{
    void DoAction();
}
public class Dog : IObject
{
    public void DoAction()
    {
        this.Wang();
    }

    public void Wang()
    {
        Console.WriteLine("{0} Wang~Wang~", this.GetType().Name);
    }
}
public class Mouse : IObject
{
    public void DoAction()
    {
        this.Run();
    }
    public void Run()
    {
        Console.WriteLine("{0} Run", this.GetType().Name);
    }
}
public class Baby : IObject
{
    public void DoAction()
    {
        this.Cry();
    }

    public void Cry()
    {
        Console.WriteLine($"{0} Cry", this.GetType().Name);
    }
}
public class Father : IObject
{
    public void DoAction()
    {
        this.Roar();
    }
    public void Roar()
    {
        Console.WriteLine("{0} Roar", this.GetType().Name);
    }
}
public class Mother : IObject
{
    public void DoAction()
    {
        this.Wispher();
    }
    public void Wispher()
    {
        Console.WriteLine("{0} Wispher", this.GetType().Name);
    }
}
public class Neighbor : IObject
{
    public void DoAction()
    {
        this.Awake();
    }
    public void Awake()
    {
        Console.WriteLine("{0} Awake", this.GetType().Name);
    }
}
public class Stealer : IObject
{
    public void DoAction()
    {
        this.Hide();
    }
    public void Hide()
    {
        Console.WriteLine("{0} Hide", this.GetType().Name);
    }
}
/// <summary>
/// 一只猫的故事……
/// 在一个月黑风高的晚上
/// 一只猫叫了一声,然后
/// 狗叫了,老鼠跑了……
/// </summary>
public class Cat
{
    /// <summary>
    /// 代码中依赖太重,中间涉及到Dog等,任何一个修改,都需要修改猫,会造成这只猫非常地不稳定
    /// 这根本就不是我们写代码的正确姿势
    /// 猫就是猫,只是负责自己的事情,只能喵一声,其他动作跟猫完全无关
    /// 在这里猫只是喵一声,这仅仅是一个触发动作,后续究竟要做什么,猫不用管
    /// </summary>
    public void Miao()
    {
        Console.WriteLine($"{this.GetType().Name}Miao~wu~");
        new Dog().Wang();
        new Mouse().Run();
        new Baby().Cry();
        new Mother().Wispher();
        new Father().Roar();
        new Neighbor().Awake();
        new Stealer().Hide();
    }
    /// <summary>
    /// 正确的姿势:应该是猫叫了以后,触发一部分动作,这部分动作不会影响到猫
    /// 触发一部分动作其实就是去执行一连串方法,那该怎么办?——多播委托
    /// 
    /// 发现通过应用多播委托以后,猫变得稳定了,无论Dog怎么修改,都不会影响到猫
    /// 实现了让猫只是做自己的事情,其它的事情丢给外部去指定,代码稳定了
    /// </summary>
    public Action ActionHandler;
    public void MiaoByDelegate()
    {
        Console.WriteLine($"{this.GetType().Name}喵了一声,并通过多播委托触发了以下一系列动作……");

        //if (ActionHandler != null)
        //{
        //    ActionHandler.Invoke();
        //}

        //这个问号表示,如果ActionHandler不为空就执行Invoke方法,效果同上面的if相同
        ActionHandler?.Invoke();

        Console.WriteLine("喵完就跑……");
    }
    /// <summary>
    /// 事件:事件和委托好像没有太大变化,只是在委托的前面加了event关键字
    /// 事件和委托的区别?
    /// 事件其实是委托加一个event关键字,事件只能在声明当前事件的类内部Invoke
    /// 即使是在子类也不可以
    /// 这样就更加安全
    /// 委托是一个类,事件其实是委托的一个实例,只是事件比委托更加安全
    /// 
    /// 事件的应用真的是无处不在,AspNetCoreMVC管道、WinForm
    /// </summary>
    public event Action EventHandler;
    public void MiaoByEvent()
    {
        Console.WriteLine($"{this.GetType().Name}喵了一声,并通过事件触发了以下一系列动作……");

        if (EventHandler != null)
        {
            EventHandler.Invoke();
        }

        EventHandler?.Invoke();

        Console.WriteLine("喵完赶紧跑……");
    }
    /// <summary>
    /// 观察者模式:甩锅大法,就是把不稳定的东西都丢出去,保证自身稳定
    /// </summary>
    private List<IObject> list = new List<IObject>();
    public void AddObserver(IObject observer)
    {
        list.Add(observer);
    }
    public void MiaoByObserver()
    {
        Console.WriteLine($"{this.GetType().Name}喵了一声,并通过观察者模式触发了一系列动作……");
        foreach(IObject obj in list)
        {
            obj.DoAction();
        }
        Console.WriteLine("喵完   赶紧、立刻、马上   跑……");
    }
}
public void ShowUseMuliDele()
{
    Console.WriteLine();
    Console.WriteLine("----------------最烂的代码写法----------------");
    Console.WriteLine();
    new Cat().Miao();

    Console.WriteLine();
    Console.WriteLine("----------------多播委托----------------");
    Console.WriteLine();
    Cat cat = new Cat();
    Action action = cat.ActionHandler;
    Action action = new Action(new Dog().DoAction);
    action += new Mouse().DoAction;
    action += new Baby().DoAction;
    action += new Mother().DoAction;
    action += new Father().DoAction;
    action += new Neighbor().DoAction;
    action += new Stealer().DoAction;
    cat.ActionHandler = action;
    cat.MiaoByDelegate();

    Console.WriteLine();
    Console.WriteLine("----------------事件----------------");
    Console.WriteLine();
    cat.EventHandler += new Dog().DoAction;
    cat.EventHandler += new Mouse().DoAction;
    cat.EventHandler += new Baby().DoAction;
    cat.EventHandler += new Mother().DoAction;
    cat.EventHandler += new Father().DoAction;
    cat.EventHandler += new Neighbor().DoAction;
    cat.EventHandler += new Stealer().DoAction;
    cat.MiaoByEvent();

    Console.WriteLine();
    Console.WriteLine("----------------观察者模式----------------");
    Console.WriteLine();
    cat.AddObserver(new Dog());
    cat.AddObserver(new Mouse());
    cat.AddObserver(new Baby());
    cat.AddObserver(new Mother());
    cat.AddObserver(new Father());
    cat.AddObserver(new Neighbor());
    cat.AddObserver(new Stealer());
    cat.MiaoByObserver();
}

多播委托的应用

简单的标准事件订阅与发布

public class EventStandard
 {
     public void Show()
     {
         Console.WriteLine();
         FrameworkClass frameworkClass = new FrameworkClass()
         {
             Id = 123,
             Name = "朝夕教育VIP架构班"
         };
         //订阅:就是把订户和事件发布者关联起来
         frameworkClass.Click_PriceIncrease += new StudentInfo().Buy;
         frameworkClass.Click_PriceIncrease += new TecentInfo().Extension;

         frameworkClass.Price = 6299;
     }
 }
 public class FrameworkClass
 {
     public int Id { get; set; }

     public string Name { get; set; }

     private int _price = 5299;

     public event EventHandler Click_PriceIncrease;
     public int Price
     {
         get
         {
             return _price;
         }

         set
         {
             //架构班涨价
             if (value > _price)
             {
                 //触发一部分动作
                 Click_PriceIncrease.Invoke(this, new FrameworkArgs()
                 {
                     OldPrice = _price,
                     NewPrice = value
                 });

                 _price = value;
             }
         }
     }
 }

 public class FrameworkArgs : EventArgs
 {
     public int OldPrice { get; set; }

     public int NewPrice { get; set; }
 }
 public class StudentInfo
 {
     public void Buy(object sender,EventArgs e)
     {
         FrameworkClass frameworkClass = (FrameworkClass)sender;
         FrameworkArgs frameworkArgs = (FrameworkArgs)e;

         Console.WriteLine($"{frameworkClass.Name}");
         Console.WriteLine($"{frameworkClass.Name}之前的价格是:{frameworkArgs.OldPrice}");
         Console.WriteLine($"{frameworkClass.Name}马上要更新的价格是:{frameworkArgs.NewPrice}");

         Console.WriteLine("马上买入……好好学习……");

         Console.WriteLine($"{frameworkClass.Name}现在的价格是:{frameworkArgs.NewPrice}");
     }
 }
 public class TecentInfo
 {
     public void Extension(object sender,EventArgs e)
     {
         FrameworkClass frameworkClass = (FrameworkClass)sender;
         FrameworkArgs frameworkArgs = (FrameworkArgs)e;

         Console.WriteLine($"{frameworkClass.Name}");
         Console.WriteLine($"{frameworkClass.Name}之前的价格是:{frameworkArgs.OldPrice}");
         Console.WriteLine($"{frameworkClass.Name}马上要更新的价格是:{frameworkArgs.NewPrice}");

         Console.WriteLine("马上推广一波……");

         Console.WriteLine($"{frameworkClass.Name}现在的价格是:{frameworkArgs.NewPrice}");
     }
 }

简单的标准事件订阅与发布

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值