事件与委托(一)
(1) 什么是委托(Delegates)?
委托是一个对象,它知道如何调用一个方法。
委托的定义: 委托类型定义了委托实例可以调用的那类方法,具体来说,委托类型定义了方法的返回类型和参数。
delegate
string
Hello(string name);
string
Splicing(string name){
return $"hello,{ name }.";
}
(2) 委托实例
- 把方法赋值给委托变量的时候就创建了委托实例。
Hello hello =
Splicing;
- 调用
string resultName = hello("bob");//输出 hello bob
- 委托的实例其实就是调用者的委托:调用者调用委托,然后委托调用目标方法。
- 间接的把调用者和目标方法解耦合了。
- 方法是在运行时才赋值给委托变量的。
(3) 多播委托
所有的委托实例都具有多播的能力。一个委托实例可以引用一组目标方法。
引用一组方法的示例:
- +和+= 还有 -和-=操作符号可以合并和解除委托实例。
DemoDelegate dd = DemoMethond1; DemoDelegate dd += DemoMethond2; - 调用委托dd就回调用DemoMethond1和DemoMethond2
- 委托的调用顺序与他们的定义顺序一致
- 和 -=会把右边的委托从左边的委托里移除
DemoDelegate dd -=
DemoMethond1;
- 委托变量使用+或+=操作符时,其操作数可以是null。就相当于把一个新的值赋给了委托变量。
DemoDelegate dd = null; dd += DemoMethond1;
相当于
dd =
DemoMethond1;
- 对单个目标方法的委托变量使用-=操作符时,就相当于把null值赋值给了委托变量。
- 委托是不可变的
- 使用-=或+=操作符时,实际上时创建了新的委托实例,并把它赋给当前的委托变量。
- 如果多播委托的返回值不是void,那么调用者从最后一个被调用的方法来接收返回值。前面的方法仍然会被调用,但是其返回值就被弃用了。
- 所有的委托类型都派生于system.MulticastDelegate,而它又派生于system.delegate
- c#会把作用于委托的+、-、-=、+=操作编译成使用system.delegate的combine和remove两个静态方法。
实例方法目标和静态方法目标
- 当一个实例方法被赋值给委托对象的时候,这个委托对象不仅要保留着对方法的引用,还要保留着方法所属实例的引用。
- system.delegate的Target属性就代表着这个实例。
- 如果引用的是静态方法,那么Target属性值就是null。
public
delegate
void
Reporter(int result);
public
class A
{
public
void
AReporter(int result)
{
Console.WriteLine(result);
}
}
public
class
Program
{
static
void
Main()
{
A a =
new A();
Reporter r = x.AReporter;
r(50);
Console.WriteLine(r.Target
== x);//True
Console.WriteLine(p.Method);//void AReporter(Int32)
}
}
(4) 泛型委托
- 委托类型可以包含泛型类型
public
delegate T Mydelegate<T>(T parameter);
Func和Action委托
- 使用泛型委托,他们可以调用的方法可以拥有任意返回值类型和任意合理数量的参数。
//1.这里表示的是,只有一个带泛型T返回值类型的委托
delegate
TResult
Func<out
TResult>();
//2.这里表示的是,有一个带泛型T返回值类型和一个带泛型入参的委托(入参数量最多16个)
delegate
TResult
Func<in T,out
TResult>(T parameter);
//3.这里表示的是,一个无参无返回值类型的委托
delegate
void
Action();
//4.这里表示的是,一个入参无返回值类型的委托(入参数量最多16个)
delegate
TResult
Func<in T,out
TResult>(T parameter);
(5) 委托and接口
- 委托可以解决的问题,接口都可以解决。
- 什么情况下更适合委托而不是接口呢?当下列条件满足其中之一时:
- 接口只能定义一个方法
- 需要多播能力
- 订阅者需要多次实现接口
class A :
ICalculation
{
public
int
Calculation(int x)=> x*x;
}
class B :
ICalculation
{
public
int
Calculation(int x)=> x*x*x;
}
<
(6) 委托的兼容性
- 委托类型之间互不相容,即使方法签名一样
delegate
void
Demo1();
delegate
void
Demo2();
Demo1 d1 =
Method1;
Demo2 d2 = d1;// error
- 如果委托实例拥有相同的方法目标,那么委托实例就认为是相等的。
- 当你调用一个方法时,你提供的参数(argument)可以比方法的参数(parameter)定义更具体。
- 委托可以接受比她的方法目标更具体的参数类型,这个叫ContraVariance
- 和泛型参类型一样,委托的Variance仅支持引用转换
delegate
void
StringAction(string s);
class
Demo
{
static
void
Main()
{
StringAction sa =
new
StringAction(ActOnObject);
sa("hello");
}
static
void
ActOnObject(object o)=>Console.WriteLine(o);//hello
}
- 调用方法时,你可以得到一个比请求的类型更具体的类型返回结果。
- 委托的目标方法可以返回比委托描述里更具体的类型的返回结果Covariance.
- Covariance,out; ContraVariance,in;
delegate
object
ObjectRetriever(string s);
class
Demo
{
static
void
Main()
{
ObjectRetriever o =
new
ObjectRetriever(RetrieverString);
object result = o();
Console.WriteLine(result);
}
static
string
RetrieverString()=>"hello";
}