C# 委托
委托---实质是一个类
委托的声明与实例化
目录
一、委托的定义
二、委托的声明
1、声明委托
2、在该类中定义方法,参数返回值与声明的委托对应
3、委托的实例化与调用
4、委托的本质
三、委托的应用场景
四、代码地址
一、委托的定义
将方法当作参数传递,就是委托。
二、委托的声明
委托类似于方法,可以有参数和返回值,但是没有方法体(但是委托的本质不是一个方法),需要用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);
}
结论:
如果在工作中的代码耦合严重,公共逻辑与业务逻辑之间本应该分开,可以使用委托来解耦
如果代码冗余,可以使用委托来减少冗余代码。
四、代码地址
C#高级编程之委托
————————————————
版权声明:本文为CSDN博主「菜鸟爱飞不飞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_39305029/article/details/107992992
Invoke
多播委托
调用委托
1.委托的出现,本质是因为把函数作为参数的设想。
委托(delegate)是一种引用类型,在其他语言中,与委托最接近的是函数指针,但委托不仅存储对方法入口点的引用,还存储对用于调用方法的对象实例的引用。
简单的讲委托(delegate)是一种类型安全的函数指针,首先,看下面的示例程序,在C++中使用函数指针。
上面两个函数的特点是:函数的返回值类型及参数列表都一样。那么,我们可以使用函数指针来指代这两个函数,并且可以将具体的指代过程交给用户,这样,可以减少用户判断的次数。
2. 函数的参数和返回值是声明规则的重点
3. 编译器、运行时支持这种调用才行
生成委托
编译器动态 ---生成1个方法的实例、生成1个匿名类。
对比
生成:
其实想想,你有一个工厂抽象、有N种实现
如果不带有属性的话,每个类都是对1个方法--相同参数、相同返回值 的不同内部实现。
那有N个类文件。
如果用委托,那意思是一样的,1个委托方法规则定义,N个委托实例--匿名类定义。
最终在系统中,都是N个类文件。
只是减少了生成时,自己要各种编码的费劲。
调用:
调用时,invoke,实质和调用1个静态类和静态方法,有什么区别?
其实就是一个是把方法放在了类里面,创建了类对象,然后执行类对象的方法。
一个是静态方法,直接被执行。
同样都是实现代码,被放在使用类的外面的地方。
说委托可以抽象规范,那静态方法也可以提一个最高的规范定义。
区别在:
委托更灵活。
如果一个静态方法最高抽象,有N种实现,
你在使用类使用的时候,是不是需要工厂类方法、或者设计模式、或者直接指定某个子级实现类具体的实现方法?这样写等于,还是增强了使用类和实现方法的耦合。要提前知道。
委托,我就要求一个这样输入、这样输出的,具体是啥,我不管。你实现类自己选择,随便挂哪个实现类。
其实这种需要把具体哪个实现类、或者事件对象向外暴露,作为参数,让外部传、或者让外部+= 对事件赋值。
静态方法,也可以这样做啊,一个最高抽象,外部负责传入1个方法。
不用委托,没办法传入方法。
因为编译器规定了。传入方法的,这种引用,名字就叫做委托。
除非你静态方法,传枚举参数,这用工厂类,做case,选择具体实现。
那就又绕回老路了
关键点:就是编译器支持了,把函数的引用作为参数。 名字叫做委托。