委托
委托可以把一个方法代入另一个方法,相当于指向函数的指针,换句话说,委托相当于一个函数指针
总的来说,委托是一个类,该类内部维护着一个字段,指向一个方法。
委托的声明:public delegate void DoDelegate();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void Test(object sender, EventArgs e) { Console.Write($"valueStr={sender}"); }
public delegate void DoDelegate(object sender, EventArgs e);
DoDelegate doDelegate = new DoDelegate(Test); object sender = 888; EventArgs e = new EventArgs();
doDelegate?.Invoke(sender, e);
doDelegate(sender, e);
|
泛型委托
我们每次要使用一个委托时,都需要先声明这个委托类,规定参数和返回值类型,然后才能实例化、调用。为了简化这个过程, .NET 框架为我们封装了三个泛型委托类,因此大部分情况下我们不必再声明委托,可以拿来直接实例化使用,方便了我们的日常写代码。
这三种泛型委托包括:Func委托、Action委托和Predicate委托。
Func委托
Func委托代表着拥有返回值的泛型委托。Func有一系列的重载,形式如 Func<T1,T2, … TResult>,其中TResult代表委托的返回值类型,其余均是参数类型。只有一个T时,即Func,代表该委托是无参数的。.NET封装了最多16个输入参数的Funct<>委托。
需要特别注意的是,若方法没有返回值,即返回 void ,由于 void 不是数据类型,因此不能定义Func委托。返回 void 的泛型委托见下文的Action。
Func的使用方法与一般的委托相同。例如上面的案例可改写如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| namespace delegateTest { class Program { static void Main(string[] args) { int calNum = Calculate(1, 2, Sub); Console.WriteLine("calNum:{0}", calNum); } static int Calculate(int num1, int num2, Func<int, int, int> calDel) { return calDel(num1,num2); } static int Sub(int num1, int num2) { Console.WriteLine("num1 - num2={0}", num1 - num2); return num1 - num2; } } }
|
Action委托
Action委托代表返回值为空 void 的委托,它也有一些列重载,最多拥有16个输入参数。用法与Func相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| namespace delegateTest { class Program { static void Main(string[] args) { DoSome("hello",Say); } static void DoSome(string str,Action<string> doAction) { doAction(str); } static void Say(string str) { Console.WriteLine(str); } } }
|
Predicate委托
这个一般用的比较少,它封装返回值为bool类型的委托,可被Func代替。
匿名委托
采用匿名方法实例化的委托称为匿名委托。
每次实例化一个委托时,都需要事先定义一个委托所要调用的方法。为了简化这个流程,C# 2.0开始提供匿名方法来实例化委托。这样,我们在实例化委托时就可以 “随用随写” 它的实例方法。
使用的格式是:
委托类名 委托实例名= delegate(args) { 方法体代码 };
这样就可以直接把方法写在实例化代码中,不必在另一个地方定义方法。当然,匿名委托不适合需要采用多个方法的委托的定义。
使用匿名方法可以将最初的代码改写为:
1 2 3 4 5 6 7 8 9 10 11
| public delegate void DoDelegate(object sender, EventArgs e);
DoDelegate doDelegate = delegate(sender, e) { Console.Write($"valueStr={sender}"); };
object sender = 888; EventArgs e = new EventArgs();
doDelegate(sender, e);
|
Lambda表达式
纵然匿名方法使用很方便,可惜她很快就成了过气网红,没能领多长时间的风骚。如今已经很少见到了,因为delegate关键字限制了她用途的扩展。自从C# 3.0开始,她就被Lambda表达式取代,而且Lambda表达式用起来更简单。Lambda表达式本质上是改进的匿名方法。
表达式Lambda
当匿名函数只有一行代码时,可以采用这种形式:
1 2 3 4 5 6 7 8
| public delegate void DoDelegate(object sender, EventArgs e);
DoDelegate doDelegate = (sender, e) => Console.Write($"valueStr={sender}");
object sender = 888; EventArgs e = new EventArgs();
doDelegate(sender, e);
|
语句Lambda
当匿名函数有多行代码时,只能采用语句Lambda
1 2 3 4 5 6 7 8 9 10 11 12
| public delegate void DoDelegate(object sender, EventArgs e);
DoDelegate doDelegate = (sender, e) => { Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name); Console.Write($"valueStr={sender}"); return; };
object sender = 888; EventArgs e = new EventArgs();
doDelegate(sender, e);
|
语句Lambda不能省略return。
事件
事件相当于保存委托的数组
事件是基于委托的,为委托提供一个订阅或发布的机制。事件是一种特殊的委托,调用事件和委托是一样的。
事件可以被看作是委托类型的一个变量,通过事件注册、取消多个委托和方法。
public event 委托类型 事件名称;
如:public event DoDelegate DoEvent;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void Test(object sender, EventArgs e) { Console.Write($"valueStr={sender}"); }
public delegate void DoDelegate(object sender, EventArgs e);
public event DODelegate DoEvent;
object sender = 888; EventArgs e = new EventArgs();
DoEvent += new DoDelegate(doDelegate);
DoEvent(sender, e);
DoEvent += Test;
DoEvent(sender, e);
|