一、委托的定义
将方法当作参数传递,就是委托。
二、委托的声明
委托类似于方法,可以有参数和返回值,但是没有方法体(但是委托的本质不是一个方法),需要用delegate修饰,可以在类的内部声明,也可以在类的外部声明。
1、声明委托
namespace Lanyp.Course.Delegate
{
#region delegate out of class
/// <summary>
/// 定义在类的外部的无参数无返回值的委托
/// </summary>
public delegate void NoReturnNoParaOutClass();
#endregion
/// <summary>
/// 自定义委托
/// </summary>
public class CoustomDelegate
{
#region delegate in class
/// <summary>
/// 无参数无返回值
/// </summary>
public delegate void NoReturnNoPara();
/// <summary>
/// 有参数无返回值
/// </summary>
public delegate void NoReturnHasPara(int x, int y);
/// <summary>
/// 无参数有返回值
/// </summary>
public delegate string HasReturnNoPara();
/// <summary>
/// 有参数有返回值
/// </summary>
public delegate int HasReturnHasPara(out int x, ref int y);
#endregion
}
}
2、在该类中定义方法,参数返回值与声明的委托对应
#region private methods
/// <summary>
/// 无参无返回值的函数
/// </summary>
private void NoReturnNoParaMethod()
{
Console.WriteLine("无参数无返回值的方法");
}
/// <summary>
/// 有参数无返回值的函数
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
private void NoReturnHasParaMethod(int x, int y)
{
Console.WriteLine($"{x}+{y}={x + y}");
}
/// <summary>
/// 无参数有返回值的函数
/// </summary>
private string HasReturnNoParaMethod()
{
return "无参数有返回值的方法";
}
/// <summary>
/// 有参数有返回值的函数
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
private int HasReturnHasParaMethod(out int x, ref int y)
{
x = 2;
return x + y;
}
#endregion
3、委托的实例化与调用
- 委托的实例化
委托的实例化类似与类的实例化,如NoReturnNoPara noReturnNoPara = new NoReturnNoPara(NoReturnNoParaMethod);
,但是区别在于需要将一个方法当作参数传递过去,需要注意的是,传过去的方法的参数与返回值必须与声明的委托一致。 - 执行委托的两种方式:
(1)调用Invoke函数:noReturnNoPara.Invoke();
(2)直接调用实例化对象,如noReturnNoPara();
#region public methods
/// <summary>
/// 主方法
/// </summary>
public void Show()
{
Console.WriteLine("=========================无参无返回值的委托====================");
{
//1、委托可以实例化,在实例化的过程中需要将一个方法当作参数传递过去
NoReturnNoPara noReturnNoPara = new NoReturnNoPara(NoReturnNoParaMethod);
//2、执行委托的两种方式
//noReturnNoPara.Invoke(); //方式一
noReturnNoPara();//方式二
//noReturnNoPara.BeginInvoke(null,null); //分配一个线程去执行方法,多线程中使用
//noReturnNoPara.EndInvoke(null);//多线程中使用
}
Console.WriteLine("=========================有参数无返回值的委托====================");
{
//3、实例化的时候要求传递进来的方法结构与委托一致:要求参数和返回值与委托保持一致
//NoReturnHasPara noReturnHasPara = new NoReturnHasPara(NoReturnNoParaMethod); //错误示范
NoReturnHasPara noReturnHasPara = new NoReturnHasPara(NoReturnHasParaMethod); //正确示范
//noReturnHasPara.Invoke(1, 3);//方式一
noReturnHasPara(1, 3);//方式二
}
Console.WriteLine("=========================无参数有返回值的委托====================");
{
HasReturnNoPara hasReturnNoPara = new HasReturnNoPara(HasReturnNoParaMethod);
string result = hasReturnNoPara.Invoke();
Console.WriteLine(result);
}
Console.WriteLine("=========================有参数有返回值的委托====================");
{
HasReturnHasPara hasReturnHasPara = new HasReturnHasPara(HasReturnHasParaMethod);
int x = 0;
int y = 3;
int result = hasReturnHasPara.Invoke(out x, ref y);
Console.WriteLine($"{x}+{y}={x + y}");
}
Console.ReadKey();
}
4、委托的本质
委托的本质是一个类,继承自MulticastDelegate特殊类
三、委托的应用场景
思考:有两个学生,分别是北京人和上海人,他们的方言不同,如何实现不同地区的同学向别人打招呼?
- 方案一:利用枚举,优势在于没有冗余代码,但是缺点在于每次新增地名都需要修改枚举和方法,造成代码不稳定,耦合程度高
- 方案二:每次新增一个地方就新增一个方法:相对于方案一优势在于不用修改太多的代码,耦合低,稳定性更高,缺点在于冗余代码太多
/// <summary>
/// 如果需要实现不同的地区的同学打招呼,有以下两种常规方案
/// </summary>
public class SayHi
{
#region 方案一:利用枚举,优势在于没有冗余代码,但是缺点在于每次新增地名都需要修改枚举和方法,造成代码不稳定,耦合程度高
public enum From
{
Shanghai,
Beijing
}
/// <summary>
/// 利用枚举约束,使调用者尽量少出错
/// </summary>
/// <param name="name"></param>
/// <param name="from"></param>
public void SayAction(string name, From from)
{
Console.WriteLine("招招手~~~~");
switch (from)
{
case From.Shanghai:
Console.WriteLine($"{name}:侬好!");
break;
case From.Beijing:
Console.WriteLine($"{name}:你好!");
break;
default:
throw new Exception("From type was wrong!");
}
}
#endregion
#region 方案二:每次新增一个地方就新增一个方法:相对于方案一优势在于不用修改太多的代码,耦合低,稳定性更高,缺点在于冗余代码太多
public void ShangHai(string name)
{
Console.WriteLine("招招手~~~~");
Console.WriteLine($"{name}:侬好!");
}
public void Beijing(string name)
{
Console.WriteLine("招招手~~~~");
Console.WriteLine($"{name}:你好!");
}
#endregion
}
那么有没有更好的解决方案呢?
- 终极解决方案:行为设计模式,利用委托传进来不同的方法,执行不同地区的学生的打招呼的方式
- 这样做的优点:
1、增加公共业务逻辑方便,只需要在公共方法内部增加即可,既减少代码的冗余,又最大程度保证程序的稳定
2、增加一个新的业务逻辑模块(新增一个地区的人打招呼),逻辑由调用者(Student)提供,可以做到逻辑解耦
/// <summary>
/// 委托
/// </summary>
/// <param name="name"></param>
public delegate void SayHiDelegate(string name);
/// <summary>
/// 扩展方法
/// </summary>
public static class StudentExtension
{
#region 调用者提供的业务逻辑
public static void ShangHai(this Student student, string name)
{
Console.WriteLine($"{name}:侬好!");
}
public static void HongKong(this Student student, string name)
{
Console.WriteLine($"{name}:雷猴!");
}
public static void Beijing(this Student student, string name)
{
Console.WriteLine($"{name}:你好!");
}
#endregion
#region 公共方法
/// <summary>
/// 终极解决方案:行为设计模式,利用委托传进来不同的方法,执行不同地区的学生的打招呼的方式
/// 优势:
/// 1、在该方法中可以增加公共业务逻辑,可以减少代码的冗余
/// 2、增加一个新的业务逻辑模块(新增一个地区的人打招呼),逻辑由调用者提供,可以做到逻辑解耦
/// </summary>
/// <param name="student"></param>
/// <param name="name"></param>
/// <param name="sayHiDelegate"></param>
public static void Action(this Student student, string name, SayHiDelegate sayHiDelegate)
{
Console.WriteLine("招招手~~~");
sayHiDelegate.Invoke(name);
}
#enregion
}
调用时,只需要将各个不同地区的打招呼方式的扩展方法传递到委托中即可
{
//上海人打招呼
Console.WriteLine("======================上海人打招呼====================");
Student student = new Student()
{
Name = "张三"
};
SayHiDelegate studentDelegate = new SayHiDelegate(student.ShangHai);
student.Action(student.Name, studentDelegate);
}
{
//北京人打招呼
Console.WriteLine("======================北京人打招呼====================");
var student = new Student()
{
Name = "王五"
};
var studentDelegate = new SayHiDelegate(student.Beijing);
student.Action(student.Name, studentDelegate);
}
结论:
- 如果在工作中的代码耦合严重,公共逻辑与业务逻辑之间本应该分开,可以使用委托来解耦
- 如果代码冗余,可以使用委托来减少冗余代码。