内容:
第四章(上): Advanced C#
- Delegate
- Events
- Lamda
- Anonymous Methods
- Exception Handler
- Enumeration & Iterartor
- Nullable Types
- Extension Methods
- Anonymous Types
1. Delegate
一个delegate type定义了 delegate 实例可以调用的方法类型。
例子:
// 这里定义了一个delegate类型,规定输入输出的参数类型
delegate int Transformer (int x);
class Test
{
static void Main()
{
//Transformer t = new Transformer (Square);
Transformer t = Square; // Create delegate instance
//t.Invoke(3)
int result = t(3); // Invoke delegate
Console.WriteLine (result); // 9
}
static int Square (int x) => x * x;
}
1.1 Writing Plug-in Methods with Delegates
就是把委托当做函数的参数, 这个函数就叫做高阶函数: high-order function
1.2 Multicast Delegates☆
每个delegate instance可以引用不止一个target method:
SomeDelegate d = SomeMethod1;
d += SomeMethod2;
注意:
- 调用delegate instance的顺序,会和 添加引用顺序一致;
- delegate 是不可变的,当使用
+=
和-=
的时候,实际上是创建了一个新的delegate instance,再给他赋值 - 如果被代理的method有返回值,那么调用delegate得到的返回值为最后执行的method的返回值
- 所有delegate继承自
System.MulticastDelegate
,再继承自System.Delegate
- 使用
+=
和-=
实际上会被编译成System.Delegate
的静态方法:Combine
和Remove
1.3 实例目标方法 vs. 静态目标方法
当一个delegate instance 引用了某个实例 target method的时, System.Delegate
类的Target
属性维护了这个方法的实例对象, Method
属性指向了这个方法。
class Test
{
static void Main()
{
X x = new X();
ProgressReporter p = x.InstanceProgress;
p(99); // 99
Console.WriteLine (p.Target == x); // True
Console.WriteLine (p.Method); // Void InstanceProgress(Int32)
}
}
class X
{
public void InstanceProgress (int percentComplete) => Console.WriteLine (percentComplete);
}
1.4 泛型委托
定义委托类型的时候可以使用泛型
public delegate T Transformer<T> (T arg);
1.5 Func & Action
除了 ref/out
和 pointer 参数
的 delegate,其他基本都可以用这两个替换
1.6 Delegate vs. Interface
用delegate解决的地方也能使用接口解决,那么何时使用delegate更好:
- 接口本身只定义了一个方法
- 需要multicast delegate
- subscriber需要多次实现接口
1.7 Delegate兼容性
a. 类型兼容性
delegate类型都彼此不兼容,就算他们 函数签名相同:
delegate void D1();
delegate void D2();
...
D1 d1 = Method1;
D2 d2 = d1; // Compile-time error
// 但是下面这个可以
D2 d2 = new D2 (d1);
Delegate相等的条件是,他们的target methods相等:
D d1 = Method1;
D d2 = Method1;
Console.WriteLine (d1 == d2); // True
Multicast delegates are considered equal if they reference the same methods in the same order.
b. 参数兼容性
delegate的参数具有 contravariance: (比如 传object参数的方法 给 string参数的delegate)
static void ActOnObject (object o) => Console.WriteLine (o);
...
delegate void StringAction (string s);
StringAction sa = new StringAction (ActOnObject);
c. 返回值兼容性:
delegate的参数具有 covariance: (比如 传string返回值的方法 给 object返回值的delegate)
static string RetrieveString() => "hello";
...
delegate object ObjectRetriever();
ObjectRetriever o = new ObjectRetriever (RetrieveString);
d. 泛型delegate使用variance
如果定义泛型的delegate,好的习惯是:
- 把作为返回值的type parameter表示为
out
- 把作为参数传入的 type parameter 表示为
int
2. Events
目的:
使用delegate主要是为了实现broadcaster-subscriber模式。 broadcaster内有一个 delegate instance, subscriber通过向这个delegate instance注册target method,来接收消息;broadcaster来决定何时invoke这个delegate。
Events作用:
event是C#提供的实现这个模式的语法, 它相当于是 delegate的一个子集,只提供了完成 subscriber模式必须的功能。event的存在是为了阻止 subscriber间的相互干扰。
// Delegate definition
public delegate void PriceChangedHandler (decimal oldPrice,
decimal newPrice);
public class Broadcaster
{
// Event declaration
public event PriceChangedHandler PriceChanged;
}
在上面的 Broadcaster
类中,可以像对待delegate一样对待这个event; 而在这个类的外面,只能使用+=
和-=
。
好处☆:
虽然没有event
,代码也同样执行,但是event能提供如下的安全性:
- 让其他的subscriber没有办法重新赋值
PricedChanged
,而只能使用+=
- 没法让
event
清除所有的subscriber - 没办法从外部调用event
2.1 Event内部怎么工作的
public class Broadcaster
{
public event PriceChangedHandler PriceChanged;
}
compiler会把上面的,转换成类似下面这样:
PriceChangedHandler priceChanged; // private delegate