委托(三) 委托的初始化

初始化委托对象

先定义一个委托类型, 然后使用定义的委托类型实例化一个委托对象.

1
2
3
4
5
// 定义一个委托类型
public delegate string MyDelegate(int a, int b);

// 实例化一个 MyDelegate 类型的委托对象, 此时还没有初始化
MyDelegate myDelegate;

MyDelegate myDelegate 只是实例化出了一个空的委托对象, 没有内容, 必须对其初始化. 委托的初始化方式经历了几种变化:

C# 1.0 中, 使用 "在代码中其他位置定义函数" 的方法, 显式初始化委托.

C# 2.0 引入了 匿名方法 的概念, 以一种可在委托调用中执行的 "未命名内联语句块" 的方式来初始化委托.

C# 3.0 引入了 Lambda 表达式 或者 Lambda 语句块, 这是另一种匿名方法的书写形式, 但更具表现力并且更简练.

通常, 面向 .NET Framework 3.5 或更高版本的应用程序应使用 Lambda (λ) 表达式或 Lambda 语句块来初始化委托.

  1. 第一种: [C# 1.0] 写法上和初始化类一样, 使用 New 关键字.
1
2
// 初始化 myDelegate 委托
myDelegate = new MyDelegate(program.Add);
  1. 第二种: [C# 1.0] 写法上和初始化值类型一样, 直接赋值.
1
2
// 初始化 myDelegate (直接赋值)
myDelegate = program.Add;
  1. 第三种: [C# 2.0] 使用匿名方法的原始写法进行初始化

匿名方法同样是使用 delegate 关键字定义 (强烈建议不再使用匿名方法, 了解即可, 转而使用 Lambda 表达式或 Lambda 语句块), 返回值无需特别指明, 只需使用 return 来指明返回值及其类型即可, 匿名方法参数的指明方式和普通方法的指明方式一致, 但是必须和委托定义时声明的方法签名吻合.

1
2
3
4
myDelegate = delegate (int a, int b)
{
return (a + b).ToString();
};
  1. 第四种: [C# 3.0] 使用匿名方法的简化写法--Lambda 表达式或 Lambda 语句块进行初始化 (强烈建议使用!)

Lambda 表达式和 Lambda 语句块也是委托初始化的一种手段, 它比匿名方法更为简洁, 而且不会混淆 (谁让委托类型的定义和匿名方法的定义使用同样的关键字的, 唉... 乱套了吧... 我瞎说的~~), 总之正如微软文档说的一样, 学习使用 lambda 表达式吧, 为什么呢? 因为如果你不会的话, 连别人的程序都看不懂啊😥

Lambda

  1. Lambda 的特征符号是: =>. => 符号的左边是参数, 使用小括号 ( ) 括起来, 右侧是语句或表达式, 当右侧是语句时, 必须使用大括号 { } 括起来, 当右侧是表达式时, 则不能带有大括号, 且表达式只能有一句.

  2. Lambda 中右侧是语句时, 使用 return 语句来指明返回值及其类型, 返回值的类型必须符合委托的要求.

    1
    Func<int, int, int> funA = (int x, int y) => { return x + y; };
  3. Lambda 中参数的类型可以注明, 如上面的例子, 也可以省略, 如下面的例子.

    1
    Func<int, int, int> funB = (x, y) => { return x * y; };
  4. Lambda 中的参数, 当且仅当只有一个时, 小括号可以省略.

    1
    2
    3
    4
    5
    Func<int, int> funC = x => { return x * 2; }; // 当且仅当只有一个时, 小括号可以省略.

    Func<float> funD = () => { return 3.1415926f; }; // 零个参数时, 小括号不可以省略.

    Func<int, int, int, int> funE = (x, y, z) => { return x * 2 + y * 3 + z * 4; }; // 两个及以上参数时, 小括号不可以省略.
  5. Lambda 中右侧是表达式时, 大括号必须去掉, 并且此时 return 也必须省略, 因为 return 是语句, 不是表达式.

    1
    Func<int, int, int> funF = (x, y) => x * y; // 大括号和 return 必须同时存在, 或同时去掉.
  6. 大括号存在时, 大括号中只能写 语句. 此时称为: Lambda 语句块. 或者说当书写语句时, 必须用 语句块符号 { } 括起来.

  7. 大括号不存在时, 则只能写 表达式, 并且 表达式只能写一句 . 此时称为: Lambda 表达式.

    1
    2
    3
    4
    5
    6
    myDelegate = (a, b) =>
    {
    return (a + b).ToString();
    }; // Lambda 语句块

    Action action = () => Console.WriteLine("action"); // Lambda 表达式

多播委托

前面讲了委托的四种初始化方式全部都是使用了 = 运算符, 其实委托还可以通过 += -= 运算符进行注册和取消注册, 而且可以实现多播委托.

  • 多播委托: 一个委托实例一次性委托多个实例方法, 调用委托时会依次调用所有被委托的方法.

  • 委托不仅仅可以 (取消) 注册方法, 也可以 (取消) 注册委托!

下表是 +- 的运算规则:

Expression Result
null + d1 d1
d1 + null d1
d1 + d2 [d1, d2]
d1 + [d2, d3] [d1, d2, d3]
[d1, d2] + [d2, d3] [d1, d2, d2, d3]
[d1, d2] - d1 d2
[d1, d2] - d2 d1
[d1, d2, d1] - d1 [d1, d2]
[d1, d2, d3] - [d1, d2] d3
[d1, d2, d3] - [d2, d1] [d1, d2, d3]
[d1, d2, d3, d1, d2] - [d1, d2] [d1, d2, d3]
[d1, d2] - [d1, d2] null