- 本文已参与「新人创作礼」活动,一起开启掘金创作之路。
什么是委托?
委托在C#中应该是无处不在的,它是C#的精髓所在,我们可以将它理解为函数指针。
如何使用委托?
- 定义委托
- 创建委托实例,并指向一个方法
- 调用
public class Program
{
public delegate int CalcDelegate(int number1,int number2);
static void Main(string[] args)
{
CalcDelegate catBehavior = Add;
int calcResponse = catBehavior.Invoke(1,2);
Console.WriteLine($"计算值为:{calcResponse}");
}
public static int Add(int number1, int number2)
{
return number1 + number2;
}
}
使用委托有什么好处?
1. 代码复用
下面是实现一个类似计算器功能的类。
public class Program
{
static void Main(string[] args)
{
Console.WriteLine($"计算值为:{Calc(1,2,Calcoperation.Add)}");
}
public static double Calc(double number1, double number2,Calcoperation calcoperation)
{
switch (calcoperation)
{
case Calcoperation.Add:
return Add(number1, number2);
case Calcoperation.Subtract:
return Subtract(number1, number2);
case Calcoperation.Multiply:
return Multiply(number1, number2);
default:
return 0;
}
}
public static double Add(double number1, double number2)
{
return number1 + number2;
}
public static double Subtract(double number1, double number2)
{
return number1 - number2;
}
public static double Multiply(double number1, double number2)
{
return number1 * number2;
}
public enum Calcoperation { Add,Subtract,Multiply,Divide }
}
思考: 在这里如果添加一个算法,那么Switch需要加一个节点,然后需要加枚举,还要添加对应的方法?如果使用委托,直接讲算法当初参数传递会是什么样的呢?
public class Program
{
public delegate double CalcDelegate(double number1, double number2);
static void Main(string[] args)
{
Console.WriteLine($"计算值为:{Calc(1,2,Add)}");
}
public static double Calc(double number1, double number2, CalcDelegate calcDelegate)
{
return calcDelegate.Invoke(number1, number2);
}
public static double Add(double number1, double number2)
{
return number1 + number2;
}
public static double Subtract(double number1, double number2)
{
return number1 - number2;
}
public static double Multiply(double number1, double number2)
{
return number1 * number2;
}
}
2. 异步调用
委托的异步调用主要是靠BeginInvoke和EndInvoke实现的,下面请看代码。关于这两个方法的具体实现,大家可以去查阅源码,可能会让你的感受更加深刻
public class Program
{
public delegate int CalcDelegate(int number1, int number2);
static void Main(string[] args)
{
CalcDelegate catBehavior = Add;
IAsyncResult result = catBehavior.BeginInvoke(1, 2, null, null);
while (!result.IsCompleted)
{
Console.WriteLine("Unfinished");
}
Console.WriteLine(catBehavior.EndInvoke(result));
}
public static int Add(int number1, int number2)
{
Thread.Sleep(2000);
return number1 + number2;
}
}
我这里的Add方法里面使线程休眠了2s,如果在同步方法里面,用户需要等待执行结果这样的体验是非常不好的。
在这里可以看出我们使用BeginInvoke执行了异步委托,使用EndInvoke获取异步的结果。
思考: 这里如果异步线程一直未完成,EndInvoke将一直阻塞调用线程,直到到异步调用完成?
3. 回调
上面说到如果异步线程未完成,我们应该怎么处理呢?大家可以看到我上面代码中使用的BeginInvoke方法我填入了两个NULL值。
通过上图可以看出来,BeginInvoke方法里面后面的两个参数是AsyncCallback,Object类,AsyncCallback主要是用于通知异步执行的回调,Object主要是用于向回调传递的参数。
public class Program
{
public delegate int CalcDelegate(int number1, int number2);
static void Main(string[] args)
{
CalcDelegate catBehavior = Add;
string message = "传递消息!";
IAsyncResult result = catBehavior.BeginInvoke(1, 2,new AsyncCallback(CallBack), message);
for (int i = 0; i < 6; i++)
{
Thread.Sleep(1000);
}
}
public static int Add(int number1, int number2)
{
Thread.Sleep(3000);
return number1 + number2;
}
public static void CallBack(IAsyncResult async)
{
AsyncResult asyncResult = (AsyncResult)async;
CalcDelegate calc = (CalcDelegate)asyncResult.AsyncDelegate;
int calcResult = calc.EndInvoke(asyncResult);
string message = asyncResult.AsyncState.ToString();
Console.WriteLine($"异步委托执行结果值为:{calcResult},异步委托最后携带的参数为:{message}");
}
}
给大家看下最后的执行结果
总结
关于线程问题,我们可以放到后续的文章中再来交流,本文只是我的一些个人观点,如果有写的不好的地方,还请大家多多指正。这里也只是委托的一小部分基础,关于委托在我们平时的代码里也是时常可见,声明委托也有许多种方式,如Action是无参无返回值的委托 Func是无参有返回值的委托等等... 委托还有非常多的用法,感兴趣的朋友可以多去了解了解。