委托-lambda-linq-事件

该博文是根据Eleven老师的视频资源总结的,要看Eleven老师的视频请点我

委托

1.委托的本质是一个类。证明如下:

//声明一个委托

namespace MyTestConsole
{
    public class MyDelegateTest
    {
        public delegate void NoReturnNoParameter();
    }
}

证明1: 上面的委托经过反编译生成的IL代码如下:
.class public auto ansi beforefieldinit MyTestConsole.MyDelegateTest
    extends [mscorlib]System.Object
{/
    // Nested Types
    .class nested public auto ansi sealed NoReturnNoParameter
        extends [mscorlib]System.MulticastDelegate  
    {/
        // Methods
        .method public hidebysig specialname rtspecialname 
            instance void .ctor (
                object 'object',
                native int 'method'
            ) runtime managed 
        {
        } // end of method NoReturnNoParameter::.ctor

        .method public hidebysig newslot virtual 
            instance void Invoke () runtime managed 
        {
        } // end of method NoReturnNoParameter::Invoke

        .method public hidebysig newslot virtual 
            instance class [mscorlib]System.IAsyncResult BeginInvoke (
                class [mscorlib]System.AsyncCallback callback,
                object 'object'
            ) runtime managed 
        {
        } // end of method NoReturnNoParameter::BeginInvoke

        .method public hidebysig newslot virtual 
            instance void EndInvoke (
                class [mscorlib]System.IAsyncResult result
            ) runtime managed 
        {
        } // end of method NoReturnNoParameter::EndInvoke

    }/ // end of class NoReturnNoParameter


    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x2196
        // Code size 8 (0x8)
        .maxstack 8

        // {
        IL_0000: ldarg.0
        // (no C# code)
        IL_0001: call instance void [mscorlib]System.Object::.ctor()
        // }
        IL_0006: nop
        IL_0007: ret
    } // end of method MyDelegateTest::.ctor

}/ // end of class MyTestConsole.MyDelegateTest

从上面可以看到声明的委托public delegate void NoReturnNoParameter();经过反编译生成的IL语言,从IL语言中可以知道委托被反编译成了类,该类中包含了一个.ctor的构造函数和三个方法:Invoke()、BeginInvoke()、EndInvoke(). 其中System.MulticastDelegate是一个特殊类只能被声明的委托自动继承,不能被声明的其他类所继承.

证明2:

class Program
{
    static void Main(string[] args)
    {
        Type type = typeof(MyDelegateTest.NoReturnNoParameter);
        Console.WriteLine(type.IsClass);  //会输出true,此处也证明了委托是一个类
        Console.ReadKey();
    }
}

2.委托的声明、实例化和调用。

namespace MyTestConsole
{
    public delegate void NoReturnNoParameter();
    public delegate string WithReturnAndParameter(int year);
    public delegate int WithReturnAndParameter2(ref int year, out int month);//设置可以是这样的
    public class Student
    {
        public void Show1()
        {
            Console.WriteLine("do something");
        }
        public string Show2(int year)
        {
            {
                //逻辑代码
            }
            return "Value";
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Student student = new Student();
        NoReturnNoParameter method1 = new NoReturnNoParameter(student.Show1);//实例化委托
        WithReturnAndParameter method2 = new WithReturnAndParameter(student.Show2);
        method1.Invoke();    //委托的调用   或者写成:method1();
        string s = method2.Invoke(5);
        //下面两个这里就不介绍了,请看多线程专题
        method1.BeginInvoke(null, null);//委托的异步调用
        method1.EndInvoke(null); //委托的异步调用的等待
    }
}

3.委托存在的意义:

        a.委托可以把一个方法包裹成变量传进另一个方法中,供另一个方法调用。

        b.通过委托开启多线程

当不使用委托时,下面的代码只能存在缺陷

namespace MyTestConsole
{
    public enum Language
    {
        Chinese,
        English,
        Japanese
    }
    //不使用委托时一个学生向说不同语言的交流生问好时的两种方式(都存在缺陷)
    public class Student
    {
        //第一种方式 
        //缺陷:代码不稳定,存在耦合
        public void SayHi(string name,Language language)
        {
            Console.WriteLine("开始问候"); //添加监控
            switch (language)
            {
                case Language.Chinese:
                    Console.WriteLine("你好"+name);
                    break;
                case Language.English:
                    Console.WriteLine("Hello" + name);
                    break;
                case Language.Japanese:
                    Console.WriteLine("gou ni ji wa" + name);
                    break;
                default:
                    break;
            }
            Console.WriteLine("结束问候");
        }
        //第二种方式
        //虽然解耦了,但是存在代码重复
        public void SayHiByChinese(string name)
        {
            Console.WriteLine("开始问候");
            Console.WriteLine("你好" + name);
            Console.WriteLine("结束问候");
        }
        public void SayHiByEnglish(string name)
        {
            Console.WriteLine("开始问候");
            Console.WriteLine("Hello" + name);
            Console.WriteLine("结束问候");
        }
        public void SayHiByJapanese(string name)
        {
            Console.WriteLine("开始问候");
            Console.WriteLine("gou ni ji wa" + name);
            Console.WriteLine("结束问候");
        }
    }
}

通过委托存在的意义a项对代码进行优化

namespace MyTestConsole
{
    public enum Language
    {
        Chinese,
        English,
        Japanese
    }
    //使用委托进行代码优化如下
    //代码即稳定了(逻辑解耦),又实现了代码复用
    public delegate void SayHi(string name);    
    public class Student
    {
        public void SayHiPerfecet(string name, SayHi sayHi)
        {
            Console.WriteLine("开始问候");
            sayHi.Invoke(name);  //模板方法
            Console.WriteLine("结束问候");
        }
        public void SayHiByChinese(string name)
        {
            Console.WriteLine("你好" + name);
        }
        public void SayHiByEnglish(string name)
        {
            Console.WriteLine("Hello" + name);
        }
        public void SayHiByJapanese(string name)
        {
            Console.WriteLine("gou ni ji wa" + name);
        }
    }
}

通过委托开启多线程、多播委托

class Program
{
    public delegate void NoReturnAndWithParameter(string name);
    static void Main(string[] args)
    {
        NoReturnAndWithParameter method = name => Console.WriteLine(name + "敢吃屎");
        method += name => Console.WriteLine(name + "想吃屎");
        method += name => Console.WriteLine(name + "爱吃屎");
        method.Invoke("小明");

        NoReturnAndWithParameter method2 = name => Console.WriteLine(name + "听后惊呆了");
        method2.BeginInvoke("小明",null,null);//异步多线程,多播实例不能调用,否则会异常

        foreach (NoReturnAndWithParameter item in method.GetInvocationList())
        {
            item.BeginInvoke("小明",null,null);//这样就实现了对多播委托的异步调用,nice
            item.Invoke("小明");  //也可以不异步调用
            //如果某个委托出现了异常,可以在这里添加上try进行处理,
            //不用担心整个多播委托都停止运行.
        }
        Console.ReadKey();
    }
}

委托实例的移除

class Program
{
    static void Main(string[] args)
    {
        MulticastDelegateTest test = new MulticastDelegateTest();
        test.Show();
        Console.ReadKey();
    }
}
public delegate void NoReturnAndWithParameter(string name);
public class MulticastDelegateTest
{
    public void Show()
    {
        Student student = new Student();
        NoReturnAndWithParameter method = 
        name => Console.WriteLine(name + "__name => Console.WriteLine(name+__)");
        method += this.Say;
        method += student.Say;
        method += Student.Say2;
        method += (new Student().Say3);

        method -= 
name => Console.WriteLine(name + "__name => Console.WriteLine(name+__)");  //没有移除
        method -= this.Say;
        method -= student.Say;
        method -= Student.Say2;
        method -= (new Student().Say3);  //没有移除

        method.Invoke("小明");
    }
    public void Say(string name)
    {
        Console.WriteLine(name + "__this.Say");
    }
}
public class Student
{
    public void Say(string name)
    {
        Console.WriteLine(name + "__student.Say");
    }
    public static void Say2(string name)
    {
        Console.WriteLine(name + "Student.Say2");
    }
    public void Say3(string name)
    {
        Console.WriteLine(name + "__student.Say3");
    }
}
//会输出
//小明__name => Console.WriteLine(name + __)
//小明__student.Say3

从上可以看出委托实例能否移除跟移除方法的地址和添加方法的地址是否是同一个存储地址有关

4.多播委托存在的意义

其存在意义请看下面代码

class Program
{
    static void Main(string[] args)
    {
        Cat cat = new Cat();
        //3.不使用多播委托调用
        cat.Miao();

        //4.使用多播委托调用,在这里动态的增加、减去或者调整喵咪惊叫引起的一系列反应
        cat.miaoHandler += new Dog().Wang;
        cat.miaoHandler += new Baby().Cry;
        cat.miaoHandler += new Mother().Curse;
        cat.MiaoNew();
    }
}
namespace MyTestConsole
{
    //类似于蝴蝶效应,喵咪惊叫会引起一系列的反应,如下
    public class Cat
    {
        #region 1.不使用多播委托时的Miao方法
        //此处代码有什么缺陷?
        //此处代码违反了开闭原则,当我们增加、减去或者调整喵咪惊叫引起
        //的一系列反应的时候,我们都需要修改Miao方法里面的代码,这样会
        //造成代码的不稳定
        public void Miao()
        {
            Console.WriteLine("喵咪惊叫");
            new Dog().Wang();
            new Baby().Cry();
            new Mother().Curse();
        }
        #endregion

        #region  2.使用多播委托优化Miao方法为MiaoNew
        //这样就满足了开闭原则,当增加、减去或者调整喵咪惊叫引起
        //的一系列反应的时候,MiaoNew方法里面的代码是不需要更改的.
        public Action miaoHandler;
        public void MiaoNew() 
        {
            if (miaoHandler != null)
            {
                miaoHandler.Invoke();
            }
        }
        #endregion
    }
    public class Dog
    {
        public void Wang()
        {
            Console.WriteLine("狗狗狂吠");
        }
    }
    public class Baby
    {
        public void Cry()
        {
            Console.WriteLine("婴儿大哭");
        }
    }
    public class Mother
    {
        public void Curse()
        {
            Console.WriteLine("妈妈咒骂");
        }
    }
}

题外话:

      .1.有人说多播委托中如果其中一个委托方法出了异常,他之后的方法不就执行不了了,该问题如何解决?这里拿上面的MiaoNew方法举例,请看如下解决代码

        public void MiaoNew() 
        {
            if (miaoHandler != null)
            {
                foreach (Action item in miaoHandler.GetInvocationList())
                {
                    try
                    {
                        item.Invoke();
                    }
                    catch
                    {
                        //处理代码
                    }
                }
            }
        }

        2.又有人说如果方法的参数类型和委托定义的标准不一致,要如何解决?比如下面Dog类的Wang方法有参数

    public class Dog
    {
        public void Wang(int time)
        {
            for (int i = 0; i < time; i++)
            {
                Console.WriteLine("狗狗狂吠");
            }            
        }
    }

请看如下解决代码:

Cat cat = new Cat();
cat.miaoHandler = ()=>new Dog().Wang(3);

Lambda

lambda的诞生是为委托服务的.

1.下面代码是lambda表达式演变出来的过程

namespace MyTestConsole
{
    public delegate string WithReturnAndParameter(int year);
    public class Student
    {
        public string Show(int year)
        {
            {
                //逻辑代码
            }
            return "Value";
        }
    }
    public class MyDelegateTest
    {
        public static void Show()
        {
            Student student = new Student();
            //C# 1.0时实例化委托的方式--这种方式需要一个命名方法
            WithReturnAndParameter method = new WithReturnAndParameter(student.Show);

            //C# 2.0时实例化委托的方式--这种方式就不需要必须有命名方法--匿名方法
            WithReturnAndParameter method1 = new WithReturnAndParameter(
            delegate (int year)
            {
                {
                    //逻辑代码
                }
                return "value";
            });
            WithReturnAndParameter method2 = delegate (int year)
            {
                {
                    //逻辑代码
                }
                return "value";
            };
            //C# 3.0时实例化委托的方式--这种方式由匿名方法演变过来,看起来更令人舒适--Lambda表达式
            WithReturnAndParameter method3 = new WithReturnAndParameter((int year) =>
            {
                {
                    //逻辑代码
                }
                return "value";
            });
            WithReturnAndParameter method4 = (int year) =>
            {
                {
                    //逻辑代码
                }
                return "value";
            };
        }
    }
}

总结: 什么是Lambda表达式? Lambda表达式其实就是一个方法,就是一个匿名方法。经过反编译后可以看到会在类MyDelegateTest中生成一个类中类,每个Lambda都会在这个类中类中生成一个独立的方法,然后绑定到委托的实例,所以Lambda也是一个语法糖.

2.lambda表达式的简化过程(这些简化其实都是语法糖)。

class Program
{
    public delegate string WithReturnAndParameter(int year);
    static void Main(string[] args)
    {
        //正常的Lambda表达式
        WithReturnAndParameter method = new WithReturnAndParameter((int n)=> {
            return "我敢吃屎!一吃就是" + n + "年";
        });

        //开始简化
        //1.省略Lambda的参数类型 
        //参数n的类型可以省略,参数n的类型能够根据委托约束给自动推算出来--语法糖
        //题外话: 什么是语法糖?语法糖其实就是编译器提供的快捷功能。如上,你可以
        //不声明year的类型,但是编译器能够推断出其类型,相当于你声明了类型
        //。经编译器编译后你省略的那些代码编译器会自动给你补全
        WithReturnAndParameter method1 = new WithReturnAndParameter((n) => {
            return "我敢吃屎!一吃就是" + n + "年";
        });

        //2.形参只有一个时,可以省略括号()
        WithReturnAndParameter method1 = new WithReturnAndParameter(n => {
            return "我敢吃屎!一吃就是" + n + "年";
        });

        //3.方法体只有一行可以省略大括号、分号或return--语法糖
        WithReturnAndParameter method2 = new WithReturnAndParameter(n => "我敢吃屎!一吃就是" + n + "年");

        //4.实例化委托时可以省略掉new WithReturnAndParameter --语法糖
        WithReturnAndParameter method3 = n => "我敢吃屎!一吃就是" + n + "年";
    }
}

3.基于Lambda表达式注册的多播委托是不能移除的(因为每个Lambda表达式经过编译后会生成不同的方法)

class Program
{
    public delegate void WithReturnAndParameter(string name);
    static void Main(string[] args)
    {
        WithReturnAndParameter method = name=>Console.WriteLine(name+"敢吃屎");
        method+= name => Console.WriteLine(name + "想吃屎");
        method += name => Console.WriteLine(name + "爱吃屎");

        method -= name => Console.WriteLine(name + "想吃屎");
        method -= name => Console.WriteLine(name + "爱吃屎");
        method.Invoke("小明");

        Console.ReadKey();
    }
}

会输出:
    小明敢吃屎
    小明想吃屎
    小明爱吃屎

4.C# 3.0推出的两组委托Action和Func

class Program
{
    static void Main(string[] args)
    {
        Action action = () => Console.WriteLine("无参无返回值");
        Action<string> action1 = name => Console.WriteLine(name + "会唱歌");

        Func<string> func = () => "我是返回值";
        Func<int, int> func1 = value => value + 10;

        Console.ReadKey();
    }
}

5.下面的这些就不是Lambda表达式了,而是表达式体.点我看有关表达式体的内容

//使用Lambda快捷声明方法
public class LambdaOther
{
    public string Name => "Lambda";

    public string Remark
    {
        get => "Test";
    }

    public string Get() => "Test";
}

反编译后的代码如下:

public class LambdaOther
{
    public string Name
    {
        get
        {
            return "Lambda";
        }
    }

    public string Remark
    {
        get
        {
            return "Test";
        }
    }
    public string Get()
    {
        return "Test";
    }
}

6.Lambda另一个很重要的应用--表达式目录树。

        此处就不在介绍了。

Linq

1.linq实现的原理通过下面的代码分析

public class Student
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        //step3. 获取操作数据
        List<Student> lists = new List<Student>()
        {
            new Student()
            {
                ID=1,
                Name="唐三",
                Age=18
            },
            new Student()
            {
                ID=2,
                Name="小舞",
                Age=18
            },
            new Student()
            {
                ID=3,
                Name="马红俊",
                Age=18
            }
        };
        //step1.这一句Linq是如何实现的?下面我们通过自定义类似的功能来剖析lists.Where的实现原理
        var result1 = lists.Where(o =>
        {
            Thread.Sleep(1000);
            return o.Age == 18;
        });

        Console.WriteLine("------------------------系统方法输出,会一个一个输出-----------------------------");
        foreach (var o in result1)
        {
            Console.WriteLine($"{o.Name}__{o.Age}");
        }

        Console.WriteLine("-----------------------自定义方法MyWhere输出,一下全部输出-----------------------------");

        //step4. 使用自己创建的功能类似于Linq的扩展方法
        var result2 = lists.MyWhere<Student>(o =>
        {
            Thread.Sleep(1000);
            return o.Age == 18;
        });
        foreach (var o in result2)
        {
            Console.WriteLine($"{o.Name}__{o.Age}");
        }
        //注意:MyWhere和MyWhere2获取数据的方式不同,MyWhere是一下全部获取
        //到结果并赋值给result2,所以在走到MyWhere方法处会进入该方法执行相
        //应代码,因此会停顿3秒后返回所有结果.而MyWhere2是按需获取、延时加载
        //,所以在走到MyWhere2方法处并不会进入该方法执行,而是在执行foreach
        //循环时才会进入MyWhere2方法执行,在foreach中每循环一次进入一次该方法
        var result3 = lists.MyWhere2<Student>(o =>
        {
            Thread.Sleep(1000);
            return o.Age == 18;
        });
        foreach (var o in result3)
        {
            Console.WriteLine($"{o.Name}__{o.Age}");
        }

        Console.ReadKey();
    }
}
public static class MyLinqExtension
{
    //step2. F12进去可以发现如下元数据,仿照着元数据的定义我们定义一个
    //自己的MyWhere和MyWhere2扩展方法
    //public static IEnumerable<TSource> Where<TSource>
    //(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
    public static IEnumerable<Tsource> MyWhere<Tsource>
    (this IEnumerable<Tsource> source, Func<Tsource, bool> predicate)
    {
        List<Tsource> result = new List<Tsource>();
        foreach (var one in source)
        {
            if (predicate.Invoke(one))
            {
                result.Add(one);
            }
        }
        return result.AsEnumerable();

        //上面的代码可以改成下面的代码,这样就会一个一个输出(这是因为迭代器
        //升级, 实现了实现按需获取, 延时加载)
        //foreach (var one in source)
        //{
        //    if (predicate.Invoke(one))
        //    {
        //        yield return one;
        //    }
        //}
    }
    public static IEnumerable<Tsource> MyWhere2<Tsource>(this IEnumerable<Tsource> source, Func<Tsource, bool> predicate)
    {
        //上面的代码可以改成下面的代码,这样就会一个一个输出(这是因为迭代器
        //升级, 实现了实现按需获取, 延时加载)
        foreach (var one in source)
        {
            if (predicate.Invoke(one))
            {
                yield return one;
            }
        }
    }

}

通过上面代码我们知道,Linq中where实现的原理就是把数据筛选的通用逻辑完成,把判断逻辑交给委托传递.同理Linq中的其他方法(需要委托的方法)也是基于委托的封装,把通用逻辑完成,把判断逻辑交给委托传递.

2.想看有关LINQ具体内容请点我

事件(该处有关事件的内容总结自刘铁锰老师的视频)

1.C#中什么是事件?   答:使对象或者类具备通知能力的成员

2.事件的功能 = 通知 + 可选的事件参数(即详细信息)

由事件的功能知事件被用于类或对象之间的动作协调和信息传递

3.事件模型的五个组成部分

        1.事件的拥有者(event source)

        2.事件成员(event 成员)

                事件就是一个通知别人的工具,事件自己是不会主动发生的,事件一定是被事件拥有者的某些内部逻辑触发的,触发后会通知事件的响应者对该事件进行相应的处理

        3.事件的响应者(event subscriber)

        4.事件处理器(event handler)--本质上是一个回调方法

        5.事件订阅--把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的"约定"

4.事件订阅者和事件拥有者的四种关系,及其相应关系的代码示例:

class Program
{
    static void Main(string[] args)
    {
        Timer timer = new Timer();
        timer.Interval = 1000;//设置timer对象没隔多长时间执行一次Elapsed时间,
        //单位毫秒.在这里建立订阅关系的时候,此刻Boy里面的Action方法最好让编译        
        //器生成,因为我们大多数情况下是不知道某个事件的委托约束是怎样的,
        //让编译器生成方便快捷
        timer.Elapsed += Boy.Action;
        timer.Elapsed += Girl.Action;
        timer.Start();
        Console.ReadLine();//为什么没有这一行代码,就看不到jump和sing被打印输出
    }
}
public class Boy
{
    internal static void Action(object sender, ElapsedEventArgs e)
    {
        Console.WriteLine("jump");
    }
}
public class Girl
{
    internal static void Action(object sender, ElapsedEventArgs e)
    {
        Console.WriteLine("sing"); ;
    }
}

事件拥有者是事件响应者的字段成员

class Program
{
    static void Main(string[] args)
    {
        //使用Form对象需要添加引用System.Window.Forms
        Form form = new Form();
        Controller controller = new Controller(form);
        form.ShowDialog();
    }
}
class Controller
{
    private Form form;
    public Controller(Form form)//输入ctor双击tab键自动创建构造器
    {
        if (form != null)
        {
            this.form = form;
            this.form.Click += Form_Click;
        }
    }
    private void Form_Click(object sender, EventArgs e)
    {
        this.form.Text = DateTime.Now.ToString();
    }
}

那我们该怎么办才能用代码实现上图的关系?自己创建一个MyForm类里面创建Click事件和Click事件的处理器?这样做我们还需要添加很多额外的代码,比如需要在MyForm类里添加触发事件的内部逻辑,添加实现图形化窗口的代码等. 此时我们可以让MyForm继承Form类,这样MyForm即有了Form的所有成员,又可以添加自己的方法,这样就实现了上图的关系,如下:

class Program
{
    static void Main(string[] args)
    {
        MyForm myForm = new MyForm();
        myForm.Click += myForm.Form_Click;
        myForm.ShowDialog();
    }

}
public class MyForm : Form
{
    internal void Form_Click(object sender, EventArgs e)
    {
        this.Text = DateTime.Now.ToString();
    }
}

这种关系不常见,这里就不代码展示了

5.上面都是微软官方提供的事件,下面我们来自定义事件--事件的完整声明和简略声明

class Program
{
    static void Main(string[] args)
    {
        Customer customer = new Customer();
        Waiter waiter = new Waiter();
        customer.Order += waiter.Action;
        customer.OrderDish("宫爆鸡丁","large");
        customer.PayTheBill();
        Console.ReadKey();
    }
}
//如果某个类是作为事件的参数来使用的,那么这个类最好继承EventArgs(不要问为什么)
public class OrderEventArgs:EventArgs
{
    public string DishName { get; set; }
    public string Size { get; set; }
}

// 如果你的委托是为了声明某个事件而定义的,那么委托名称最好加上后缀EventHandler.
//委托后面添加上EventHandler的意义:
//1.表明这个委托是为事件而声名的;
//2.表明这个委托是为约束事件处理器的;
//3.表明这个委托是为专门用来存储事件处理器的
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
public class Customer
{
    #region 事件的完整声明
    private OrderEventHandler orderEventHandler;
    public event OrderEventHandler Order
    {
        add
        {
            orderEventHandler += value;
        }
        remove
        {
            orderEventHandler -= value;
        }
    }
    #endregion

    #region 事件的简略声明
    //public event OrderEventHandler Order;
    #endregion

    public double Bill { get; set; }
    public void PayTheBill()
    {
        Console.WriteLine($"扫码支付{Bill}元");
    }
    public void OrderDish(string dishName,string size)
    {
        #region 事件完整声明时
        if (orderEventHandler != null)  //
        {
            OrderEventArgs e = new OrderEventArgs();
            e.DishName = dishName;
            e.Size = size;
            orderEventHandler.Invoke(this,e);  //
        }
        #endregion
        #region 事件简略声明时
        //if (Order != null)  //事件只能出现在+=和-=的左侧,而这里确出现在了!=和.Invoke的
                                //左侧,
        //{                  //这是不得已而为之,因为事件的简略声明导致无法使用委托字段进行
        //!=和.Invoke操作,所以只能使用事件来进行!=和.Invoke操作.注意在事件拥有者的外部是不
        //能使用事件进行!=和.Invoke操作.这也是语法糖导致的一个矛盾.
        //    OrderEventArgs e = new OrderEventArgs();
        //    e.DishName = dishName;
        //    e.Size = size;
        //    Order.Invoke(this, e);
        //}
        #endregion

    }
}
public class Waiter
{
    public void Action(Customer customer, OrderEventArgs e)
    {
        Console.WriteLine($"做一份{e.DishName}给顾客端上去");
        double price = 10; 
        if (e.Size=="large")
        {
            customer.Bill += price * 1.5;
        }
        else if (e.Size=="small")
        {
            customer.Bill += price * 0.5;
        }
    }
}

7. 思考:有了委托类型字段/属性,我们为什么还需要事件?

事件的本质是委托字段的包装器,这个包装器对委托字段的访问有着限制作用,就和通过属性对字段的访问起着保护和限制作用是一致的.

事件对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能,这样委托实例方法(Invoke/BeginInvoke/EndInvoke)的调用(或者说事件的触发)就只能在事件拥有者内部,这样才更符合逻辑.

//此处的代码就是因为使用的是委托而非事件,导致委托实例可以在外部任意调用而导致的问题
//--badman可以不断的替customer点餐整蛊customer
class Program
{
    static void Main(string[] args)
    {
        Customer customer = new Customer();
        Waiter waiter = new Waiter();
        customer.Order += waiter.Action;
        customer.OrderDish("宫爆鸡丁","large");
        //customer.PayTheBill();

        //如果使用委托字段,那么我们就可以在外部顺便调用该委托实例,如下:
        //这样badman就可以恶搞,可以随便点东西给customer,并把帐记在customer上.
        //事件就应该在事件拥有者的内部被触发,这样才符合逻辑,也更加安全.
        Customer badman = new Customer();    
        OrderEventArgs e = new OrderEventArgs();
        e.DishName = "满汉全席";
        e.Size = "large";
        badman.Order+= waiter.Action;
        badman.Order(customer,e);

        customer.PayTheBill();
        Console.ReadKey();
    }
}

用于声明Foo事件的委托一般命名为FooEventHandler.

FooEventHandler委托一般有两个参数,一个是object类型,名字为sender,是事件的拥有者;一个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e,是事件参数.

触发Foo事件的方法一般命名为OnFoo,即因何而发

事件的命名约定:动词或动词短语,或带有时态(ing/ed)的动词或动词短语,

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值