4:c#委托与事件(Delegate与DelegateEvent)

1.什么是委托

概念

委托的声明和方法一样,仅仅多了一个Delegate关键字;

委托的本质:

  1. 委托其实是一个类;
  2. 既然委托是一个类,那我们就可以实例化~~
  3. 只是委托在实例化的时候要求我们传递一个方法进去;

举例

声明
public delegate void First();                   //空方法委托
public delegate void Second(string sayString);  //带参数委托
public delegate int Third();                    //带返回值委托
public delegate int Fourth(out string outstr, ref string refstr);//带参数带返回值委托
public delegate void Fifth(string sayHi);       //参数委托
使用
public void Do()
{
    
    
    First first = new First(FirstMethod);
    first.Invoke();
    Second second = new Second(SecondMethod);
    second.Invoke("你好");
    Third third = new Third(ThirdMethod);
    int tResult = third.Invoke();
    Fourth fourth = new Fourth(FourthMethod);
    string outstr = "";
    string refstr = "";
    fourth.Invoke(out outstr, ref refstr);
    //把委托当做参数传递
    Fifth fifth = new Fifth(FifthMethod);
    SayHi("小安", fifth);
}
public void FirstMethod()
{
    
    
    Console.WriteLine("That is first delegate");
}
public void SecondMethod(string sayString)
{
    
    
    Console.WriteLine($"That is second delegate:{sayString}");
}
public int ThirdMethod()
{
    
    
    Console.WriteLine("That is third delegate");
    return default(int);
}
public int FourthMethod(out string outstr, ref string refstr)
{
    
    
    outstr = "1";
    outstr += "outstr:增加后";
    refstr += "refstr:增加后";
    Console.WriteLine("That is fouth delegate");
    return default(int);
}
public void FifthMethod(string name)
{
    
    
    Console.WriteLine($"That is fifth delegate:{name}");
}
public void SayHi(string name, Fifth fifth)
{
    
    
    fifth.Invoke(name);
}
public void SayHiTaowa(string name, Fifth fifth)
{
    
    
    fifth.Invoke(name);
}

2.委托作用

  1. 代码重用,去掉重复代码。
  2. 逻辑解耦。
    上面两个观点通过两个例子进行展示;
    比如打招呼的一个方法;
    ps:子曾经曰过;不要重复造轮子,所以我把老师讲的内容搬来了。
//如果业务比较简单,这二者其实没啥优劣;
        //1. 问好的时候,就是招招手~
        // 方案1:一句代码搞定了。
        // 方案2:每个方法都需要增加 招招手的业务逻辑;
        //2. 如果现在再增加一个来自于WuHan的人: 问候就是:吃了么。。。
        // 方案1:增加了分支结构,就修改了方法内部的代码,不推荐,方法修改,就需要重新调试测试;
        // 方案2:增加了一个独立的方法,对其他方法完全没啥影响。
        //如果说两个场景同时存在;
        //有没有让鱼与熊掌兼得的方案?
        //方案1 传递枚举是为了选择业务逻辑;既然是为了选择业务逻辑,那就直接传递逻辑呗;怎么传递业务逻辑?
        //业务逻辑不就时在方法里面?怎么传递方法?不就是委托么?


        //委托究竟怎么使用呢?
        //如果你在工作中遇到代码耦合很严重;  逻辑和逻辑之间应该分开,但是放在同一个方法内部的;
        //如果你的代码中出现了很多重复代码,考虑考虑委托;

        //通过委托传递业务逻辑以后:
        //一.增加公共业务逻辑方便,只需要在一个方法内部增加即可,去掉重复代码。。
        //二.如果新增一个来自武汉的人,那么逻辑由调用者提供;做到了逻辑解耦;
		//方法1,增加公共代码方便,但是如果新增加其它地方同学的话,需要增加方法中业务逻辑。
		public void SayHi(string name, FromType fromType)
        {
    
     
            Console.WriteLine("招招手~~"); 
            switch (fromType)
            {
    
    
                case FromType.YiChang:
                    Console.WriteLine($"{name}同学:你好~");
                    break;
                case FromType.ShangHai:
                    Console.WriteLine($"{name}同学:侬好~");
                    break;
                case FromType.GuangDong:
                    Console.WriteLine($"{name}同学:雷猴~");
                    break;
                case FromType.WuHan:
                    Console.WriteLine($"{name}同学:吃了么~");
                    break;
                default:
                    throw new Exception("fromType Wrong!"); //不要隐藏错误
            }
        }
//方法2,每增加一个不同地方就要新写一个方法,但是方法独立,需要加公共代码就费劲了。
      
        public void SayHiYiChang(string name)
        {
    
    
            //Console.WriteLine("招招手~~"); 
            Console.WriteLine($"{name}同学:你好~");
        }

        public void SayHiShangHai(string name)
        {
    
    
            //Console.WriteLine("招招手~~");
            Console.WriteLine($"{name}同学:侬好~");
        }
   public delegate void SayHiDelegate(string name);
    public class Students {
    
    
        //方法3、新建一个委托,把方法作为参数传入,再通过Invoke调用。
        public void SayHiAction(string name, SayHiDelegate sayHiDelegate)
        {
    
    
            Console.WriteLine("问候的同时,会招手。。。");
            sayHiDelegate.Invoke(name);
        }
        public void SayHiAction(string name, Action<string> action)
        {
    
    
            Console.WriteLine("问候的同时,会招手。。。");
            action.Invoke(name);
        }
    }

3.委托的多种实例化

{
    
    
    //直接传入方法,不带()的
    NoReturnNoPara noReturnNoPara = new NoReturnNoPara(this.NoReturnNoParaMehtod);
    noReturnNoPara.Invoke();
}
{
    
    
    //静态方法
    NoReturnNoPara noReturnNoPara = new NoReturnNoPara(DoNothingStatic);
    noReturnNoPara.Invoke();
}
{
    
    
    //实例化类,传入方法
    NoReturnNoPara noReturnNoPara = new NoReturnNoPara(new Student().Study);
    noReturnNoPara.Invoke();
}
{
    
    
    //类中静态方法
    NoReturnNoPara noReturnNoPara = new NoReturnNoPara(Student.StudyAdvanced);
    noReturnNoPara.Invoke();
}

4.系统提供委托

系统提供的委托有两种;

  1. Action
  2. Func
{
    
    
    Console.WriteLine(" ******Action/Func*************");
    //Action:是系统给我们提供的一个委托;是一个没有返回值,可以有参数的委托;最多可以有16个泛型参数;
    //Action method = new Action(this.DoNothing);
    Action<int> method = new Action<int>(DoNothingInt);
    Action<int, string> action = new Action<int, string>(DoNothingIntAndString);
    Action<int, string> action1 = DoNothingIntAndString; //这是编译器提供的便捷功能---语法糖;
    Action<int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object> action2 = null;//最多支持16个泛型参数;

    //Func:是系统给我们提供的一个委托;是一一个必须有返回值的委托,可以有参数的委托;最多可以有16个泛型参数;
    Func<int> func = new Func<int>(ToInt);
    Func<int, string> func1 = null;
    Func<int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, int, string, DateTime, object, string> func2 = null;//最多支持16个泛型参数;

    //为什么框架要给我们提供这两个委托呢?既然是框架提供的委托,自然是希望我们就使用者两个委托:
    //举例自定委托不好处;
    new Thread(new ParameterizedThreadStart(DoObject));
    ThreadPool.QueueUserWorkItem(new WaitCallback(DoObject));
    new Thread(new WaitCallback(DoObject));//这句报错了。

    //为什么?  委托的本质其实是一个类;定义的不同委托其实是不同的类,这些类没有父子级关系;不能替换,但是这些委托参数和返回值又都是一致的;
    //后面大家在使用委托的时候,就不要自己定义了,直接使用Action、Func;为了避免大家在开发工作中过多的定义委托;其实是个冗余;
    //那为什么我们还是可以自定义委托呢?  因为历史原因,因为在以前的框架中,使用了各种不同的委托;
    //C#是向前兼容;这是历史包袱;
    //在后面的作业中,就不要自己去定义委托了,必须使用acton、func;
}
如果需要在系统上委托自定,只需要加一个就可以了。
public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, in T17>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 Arg17);

        public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, in T17, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17);

5.多播委托

  1. 可以在实例化的时候通过+=添加多个方法形成方法链子;
  2. 可以在实例化的时候通过-=移除方法;

代码实现

//Console.WriteLine(" ******多播委托*************");
多播委托:我们声明的任何一个委托都是多播委托;
多播委托可以在实例化以后,可以通过+=添加和当前委托参数返回值完全一致的多个方法;形成方法链,在调用Invoke方法的时候,会按照+=的顺序依次执行方法;

NoReturnNoPara method = new NoReturnNoPara(this.DoNothing);

method += this.DoNothing;
method += DoNothingStatic;
method += new Student().Study;//不能减去,因为是不同的Student实例
method += Student.StudyAdvanced;
method += () => {
    
     Console.WriteLine("this is Lamda"); }; //lambda表达式在中间语言IL中还是生成不同的两个方法,生成的时候其实会有两个不同的名字

//method.Invoke();

多播委托:可以通过-=移除方法(必须是移除同一个方法),是按照从下往上按照顺序依次匹配,如果匹配不到,就啥也不干,只要是配到一个就移除掉,且只能移除一个;
method -= this.DoNothing;
method -= this.DoNothing;
method -= DoNothingStatic;
method -= new Student().Study;//不能减去,因为是不同的Student实例
method -= Student.StudyAdvanced;
method -= () => {
    
     Console.WriteLine("this is Lamda"); };//lambda表达式在中间语言IL中还是生成不同的两个方法,生成的时候其实会有两个不同的名字 
//如果参数不一样: 
method.Invoke();
//method.BeginInvoke(null,null);  //开启一个新的线程去完成计算;多播委托不允许异步; 
foreach (NoReturnNoPara item in method.GetInvocationList())
{
    
    
    item.BeginInvoke(null, null);
    item.Invoke();
}

6.事件Event / 观察者模式

首先看下Action,就相当于一个委托。
public delegate void Action();
事件如何声明?
/// <summary>
///事件:事件和委托好像没太大变化,只是在委托的前面加了Event 关键;
///事件和委托的区别?
///
/// 事件其实是委托+event 关键字;事件只能在声明当前这事件的类的内部Invoke;即时是在子类也不行;
/// 这样就更加安全; 
/// 委托是一个类;事件其实是委托的一个实例; 事件是比委托更加安全;
/// 
/// 事件的应用:真的是无处不在,AspNetCoreMVC管道;Winform,WPF;
/// </summary>
public event Action ActionEventHanlder;
使用事件+=方法,因事件不能直接调用,所以要在实例类中Invoke();
{
    
    
    Console.WriteLine(" ********事件的应用***********");
    Cat cat = new Cat();
    cat.ActionEventHanlder += new Dog().Wang; //狗叫了
    cat.ActionEventHanlder += new Mouse().Run;//老鼠跑了
    cat.ActionEventHanlder += new Baby().Cry; // 小孩哭了
    cat.ActionEventHanlder += new Mother().Wispher;
    cat.ActionEventHanlder += new Father().Roar;
    cat.ActionEventHanlder += new Neighbor().Awake;
    cat.ActionEventHanlder += new Stealer().Hide;
    //cat.ActionEventHanlder.Invoke();
    cat.MiaoEventHanlder();
}
观察者模式
{
    
    
    Console.WriteLine(" ******面向对象的思想实现观察者*************");
    Cat cat = new Cat();
    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.MiaoObserver();
}
{
    
    
		//Cat类中的方法
		public List<IObject> ObserverList = new List<IObject>();

        public void AddObserver(IObject observer)
        {
    
    
            ObserverList.Add(observer);
        }

        public void MiaoObserver()
        {
    
    
            Console.WriteLine("{0} MiaoObserver", this.GetType().Name);
            foreach (IObject observer in ObserverList)
            {
    
    
                observer.DoAction();
            }
        }
}

7.手写标准事件

public class EventStandard
{
    
    
    public static void Show()
    {
    
    
        FrameworkClass frameworkClass = new FrameworkClass()
        {
    
    
            Id = 1,
            Name = "VIP课程"
        };
        //订阅:就是个订户和事件发布者关联起来
        frameworkClass.PriceIncrease += new StudentInfo().Buy;
        frameworkClass.PriceIncrease += new Tencent().Extension; 
        frameworkClass.Price = 6299;
    }

    /// <summary>
    /// 订户: 关注事件,事件触发后,自己做出相应的动作
    /// </summary>
    public class StudentInfo
    {
    
    
        public void Buy(object sender, EventArgs e)
        {
    
    
            FrameworkClass frameworkClass = (FrameworkClass)sender;
            FrameworkEventArge eventArge = (FrameworkEventArge)e;
            Console.WriteLine($"this is {frameworkClass.Name}");
            Console.WriteLine($"{frameworkClass.Name}之前的价格是{eventArge.OldPrice}");
            Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
            Console.WriteLine("果断买了。。。");

            Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
        }
    }

    /// <summary>
    /// 订户,关注事件,事件触发之前,做出点动作
    /// </summary>
    public class Tencent
    {
    
    
        public void Extension(object sender, EventArgs e)
        {
    
    
            FrameworkClass frameworkClass = (FrameworkClass)sender;
            FrameworkEventArge eventArge = (FrameworkEventArge)e;
            Console.WriteLine($"this is {frameworkClass.Name}");
            Console.WriteLine($"{frameworkClass.Name}之前的价格是{eventArge.OldPrice}");
            Console.WriteLine($"{frameworkClass.Name}现在要马上更新的价格是{eventArge.NewPrice}");
            Console.WriteLine("马上推广一波。。");

        }
    }


    public class FrameworkEventArge : EventArgs
    {
    
    
        public int OldPrice {
    
     get; set; }
        public int NewPrice {
    
     get; set; }
    }

    /// <summary>
    /// 事件的发布者,发布是按并且在满足条件的情况下,触发事件
    /// </summary>
    public class FrameworkClass
    {
    
    
        public int Id {
    
     get; set; }
        public string Name {
    
     get; set; }

        private int _price=5299;

        public int Price
        {
    
    
            get
            {
    
    
                return _price;
            }
            set
            {
    
    
                if (value > _price)
                {
    
    
                    ///触发一部分动作 
                    PriceIncrease.Invoke(this, new FrameworkEventArge()
                    {
    
    
                        OldPrice = _price,
                        NewPrice = value
                    });
                }

                _price = value;
            }
        }
        public event EventHandler PriceIncrease;
    }
}

猜你喜欢

转载自blog.csdn.net/hello_mr_anan/article/details/108025731