目录

C委托和事件

C#委托和事件

一、委托

委托概念

有的开发者称委托为代理,事件委托= =事件代理。

委托是用户自定义类型,这一点和类,接口等一样的。委托是引用类型。

现实中:张三委托李四去做一件事。站在张三的角度,张三委托了李四,站在李四的角度,李四是张三的代理。

C#语言中的委托比较抽象:【 委托指向一系列具有相同签名和返回类型的方法的地址 】。

当一个方法,它的返回值和参数列表(参数个数,顺序,类型)和某个委托匹配,此委托就可以去代理此方法。

委托可以指向方法的地址。将来调用委托时,就“间接”调用了某个方法。为啥不直接调用方法呢?让控件和方法之间有一个“代理人”的角色,这样可以 让控件和方法之间耦合性降低,带来的灵活性。

总结: 委托是一系列方法地址的引用。委托就代理了一系列的方法。调用委托就相当于调用这一系列方法。

委托只所以能代理【一系列】的方法的地址。说明委托肯定实现+=,-=运算符重载。

定义委托

平时用户自定义委托的机会不太多,一般都使用C#自带的委托:

EventHandler、Action, Predicate, Func,…

2.如何定义委托?使用delegate关键字定义

观察一下:委托和方法的形式的区别:a. 没有方法体 b. 方法返回值前多一个delegate关键字。

可以没有返回,可以没有参数。

 public delegate void SayHello();
 public delegate bool Delegate2(string arg);

3。定义到哪里?

在类内部,在类外部, 但不能在方法内部

4。委托怎么使用?或者怎么调用?

想一想方法怎么调用?和方法调用比较一下。

返回值类型 返回值变量 = 方法名(实参列表);

SayHello(); // 委托不能像方法一样直接调用,为啥?没有方法体, 必须代理某个方法(即实例化)后,再调用。

a. 实例化(让某个委托类型去代理一系列方法地址)

C# 1.0写法

假如把SayHello()当成一个方法的话,Method1是另外一个方法的方法名,相当于方法的参数又是一个方法。在其他语言中,如:javascript,称为回调函数 CallBack。

委托的实例化:是把另外一个方法的方法名,传递到委托构造函数中了。

让sayHello1这个委托的实例,指向了Method1在内存分配的地址。

将来开发者调用sayHello1这个委托实例时,就相当于调用Method1这个方法,从而让需要调用Method1的其他控件和Method1不见面(解耦了)

事件肯定是委托的实例,但委托实例不一定是事件。

 SayHello sayHello1 = new SayHello(Method1); // 推荐1、Method1不能添加小括号
  public static void Method1()
 {
     Console.WriteLine("方法1");
 }

实例化有一种简写,只要方法满足了委托 (当一个方法,它的返回值和参数列表(参数个数,顺序,类型)和某个委托匹配),就可以直接赋值给委托

 SayHello sayHello2 = Method2;  // 推荐2
  public static void Method2()
 {
     Console.WriteLine("方法2");
 }

b. 使用匿名方法进行实例化,匿名委托本质就是匿名方法(匿名函数:没有名称方法) C# 2.0 写法

C#中匿名方法包括两种:1。匿名委托 2。拉姆达(表达式,语句)

当拉姆达中方法列表中只有一个形参时,()可以省略。

当拉姆达方法体中只有一条语句时,{}可以省略,只要{}省略,return必须省略

 SayHello sayHello3 = () => { Console.WriteLine("使用拉姆达表达式"); };
 SayHello sayHello3 = () => Console.WriteLine("使用拉姆达表达式");
 Delegate2 delegate2 = (arg) => { return true; };

 上面的一行代码可以简写成如下代码:
 Delegate2 delegate2 = arg => true;

匿名委托(了解)

注意:参数必须设定明确类型,且没有简写形式。

 Delegate2 delegate3 = delegate(string arg) { return false; };
 Delegate2 delegate22 = delegate (string arg)
 {
     return arg == "hello";
 };
// 代理非匿名方法,此处delegate21不是事件,第一次赋值不能使用+=
 Delegate2 delegate21 = Method3; 
  public static bool Method3(string arg)
 {
     Console.WriteLine("方法3");
     return arg == "hello";
 }

多播委托:

多播委托:一个委托实例,同时代理多个方法地址。

a. 语法细节,第一次代理时,不能使用+=,第二次及之后,需要使用+=运算符重载。

b. 注意调用时,执行顺序。

 //delegate21 = Method4; //重新赋值,把原来值替换。不叫多播委托。;
 delegate21 += Method4; // 多播委托。让一个委托实例,指向多个方法的地址。
 delegate21 -= Method3; // 移除一个委托指向
  public static bool Method4(string arg)
 {
     Console.WriteLine("方法4");
     return true;
 }

 // ......

 // c. 通过拉姆达实例化委托  C# 3.0 写法
 Delegate3 delegate31 = Method5;

 Delegate3 delegate32 = (int a, int b) =>
 {
     return (a - b).ToString();
 };
  public static string Method5(int a, int b)
 {
     return (a + b).ToString();
 }

5。委托调用

a.委托实例像方法一样调用。

b.使用Invoke()

    sayHello1();  // 调用委托,相当于访问了Method1()
    sayHello2();  // 调用委托,相当于访问了Method2()
    Console.WriteLine("-------------------------");

    sayHello1.Invoke();
    bool result = delegate22.Invoke("hello");
    Console.WriteLine(result);
    Console.WriteLine("-------------------------");

    // 多播委托调用。方法的执行顺序和委托绑定方法的顺序是一致的。
    //delegate21("hello");
    bool result2 = delegate21.Invoke("abc");
    Console.WriteLine(result2);  // 结果拿到最后一个委托的结果。



    Console.ReadKey();
}

常见委托:

 //Action,Action<T>,Func<T>,Predicate<T>,Converter<T1,T2>,EventHandler

 Action<string> action1 = (obj) => { Console.WriteLine("world"); };
 Action action2 = () => { };
 Func<string> func1 = () => { return "hello"; };
 Func<int, int, string, bool> func2 = (a, b, str) =>
 {
     Console.WriteLine(a);
     Console.WriteLine(b);
     Console.WriteLine(str);
     return false;
 };

 bool result = func2(10, 20, "hello");
 Console.WriteLine(result);

 Console.WriteLine(func1.Invoke());
 action1.Invoke("abc");

 // Arguments参数
 // 常见委托的外形:in逆变,out协变
 // public delegate void Action();
 // public delegate void Action<in T>(T obj);
 // public delegate TResult Func<out TResult>();
 // public delegate bool Predicate<in T>(T obj);
 // public delegate TOutput Converter<in TInput, out TOutput>(TInput input);
 // public delegate void EventHandler(object sender, EventArgs e);
 // public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

二、事件

1。事件概念?

现实中,事件就是发生一件事。

C#语言中的事件:事件是委托的实例(个体),委托的实例不一定是事件,但事件肯定是委托的实例。

事件是一种特殊的委托实例。特殊在哪里?安全的委托实例,受CLR(Common Language RunTime公共语言运行时)管理。

定义事件

定义一个事件。使用event关键字。 EventHandler委托类型 MyClick就是委托实例,即事件变量。

 public static event EventHandler MyClick;  // 默认值null

给委托赋值

EventHandler click1 = new EventHandler((sender, arg) => { }); // 委托实例,不是事件

3。事件定义的位置?

类内部,不能在方法内部

4。事件实例化(称事件绑定,和委托实例化基本一致。只是第一初始化时,也可以使用+=或-=)

MyClick += Program_MyClick;
MyClick += Program_MyClick1;
MyClick += delegate (object sender, EventArgs arg)
{
    Console.WriteLine("匿名委托");
};
MyClick += (sender, arg) =>
{
    Console.WriteLine("拉姆达语句");
};
 private static void Program_MyClick(object sender, EventArgs e)
 {
     Console.WriteLine("事件第一次赋值");
 }

 private static void Program_MyClick1(object sender, EventArgs e)
 {
     Console.WriteLine("事件第二次赋值");
 }

5。调用事件? 人为“立即”调用,在WinForm,一个按钮绑定事件(给事件变量赋值),事件的执行有滞后性,比如:点击时,鼠标进入时,鼠标离开时,…

//MyClick(null, null);
MyClick.Invoke(null, null);

Console.ReadKey();