委托(一) 委托的基本用法

本文重点

  1. delegate 和 Delegate 的区别

  2. 定义委托

  3. 实例化委托

  4. Action 和 Func

  5. 委托的基本用法

delegate

delegate 是一个类型关键字, 和它类似的还有: class, interface, enum 等等. 这类关键字都是用来定义的, 而不是实例化的. 枚举类型使用 enum 进行定义, 委托类型使用 delegate 进行定义. 因此 delegate 不是类! 它只是一个类型关键字! 用来定义类型的.

System.String - string 和 System.Delegate - delegate

System.String 是 .Net 框架中的一个类, .Net 框架中包含多种编程语言, 其中 C# 语言中给 System.String 起了一个别名 string, 因此 string strSystem.String str 是没有区别的, string 就是一个类, string str 这一个代码不是在定义类型, 而是在实例化对象. 但是 delegateSystem.Delegate 的关系就不是这样了. System.Delegate 是一个类, 而 delegate 则是一个 类型关键字, 它是用来定义委托的, 就像 class 定义类, struct 定义结构体, enum 定义枚举一样, 这些关键字是同一个级次的, 但他们都不是类.

如下图, String, string, Delegate 都可以调用类中的静态方法, 这是因为他们都是类, 但是 delegate 则不可以, 因为它仅仅只是一个类型关键字.

delegate

🦄如何定义一个委托类型

  1. 使用 delegate 关键字
  2. 指明待委托方法的两个特征: 1. 返回值 2. 参数
1
2
//定义委托类型, 返回值为: int, 参数为: (int, int)
public delegate int MyDelegate (int a, int b);

[] 其实 csharp 中已经有定义好的通用委托类型了, 基本无需自己定义委托类型.

🐬如何实例化出一个委托实例

实例化委托就是使用之前定义好的委托类型实例化出一个委托对象, 也叫做委托实例. 委托的实例化语法和类的实例化语法相同.

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

[] 实例化委托时通常不再使用自己定义的委托类型, 而是使用 csharp 中已经定义好的通用委托类型 Action<> 和 Func<>.

🌴如何初始化一个委托实例

  • 方法 1: 使用 new 关键字初始化. 待委托方法作为参数传入.

  • 方法 2: 直接使用符合特征的方法赋值.

[] 待委托方法如果不是静态方法, 则必须是实例方法. 不能将未实例化的方法作为参数传入委托或对委托赋值.

如何调用委托

  • 方式 1: 调用委托和调用方法在代码写法上一致.
  • 方式 2: 调用 Invoke() 方法.

委托基本用法示例 (非静态)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System;
using System.Collections.Generic;

// CSharp 起别名的语法规则, 在 namespace 外部书写时必须写全部路径
// using Package = System.Collections.Generic.Dictionary<string, int>;

namespace MyDelegateTest
{
// CSharp 起别名的语法规则, 在 namespace 内部书写的时候可以嵌套引用 namespace 外的 using
using Package = Dictionary<string, int>;

// 定义一个委托类, 指明待委托方法的特征: 1. 返回值 (string) 2. 参数 (string, int, Package)
public delegate string DelegateBuy(string itemName, int num, Package package);

class Program
{
public static void Main()
{
// 实例化 玩家包裹
Package package = new Package();

// 实例化 委托
// 委托初始化时可以直接赋值, 也可以使用 new 关键字
// 当 "待委托方法" 是非静态方法时, 只能传递待委托方法的实例作为参数, 因此这里必须实例化 Program, 并写 "program.Buy", 不能只写 "Buy".
Program program = new Program();
DelegateBuy delegateBuy = program.Buy;
DelegateBuy delegateBuy2 = new DelegateBuy(program.Buy);

// 调用委托的写法和调用方法的写法一致
Console.WriteLine(delegateBuy("体力药水", 10, package));
Console.WriteLine(delegateBuy("体力药水", 25, package));
Console.WriteLine(delegateBuy2("魔力药水", 10, package));
Console.WriteLine(delegateBuy2("魔力药水", 5, package));

// 暂停
Console.ReadKey();
}

/// <summary>
/// 待委托的方法
/// </summary>
/// <param name="itemName">物品名称</param>
/// <param name="num">物品数量</param>
/// <param name="package">玩家包裹</param>
public string Buy(string itemName, int num, Dictionary<string, int> package)
{
string str;

if (package.ContainsKey(itemName))
{
package[itemName] += num;
str = string.Format("您又购买了{0}个{1}!", num, itemName);
}
else
{
package.Add(itemName, num);
str = string.Format("您购买了{0}个{1}!", num, itemName);
}

return str;
}
}
}

委托基本用法示例 (静态)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
using System;
using System.Collections.Generic;

// CSharp 起别名的语法规则, 在 namespace 外部书写时必须写全部路径
// using Package = System.Collections.Generic.Dictionary<string, int>;

namespace MyDelegateTest
{
// CSharp 起别名的语法规则, 在 namespace 内部书写的时候可以嵌套引用 namespace 外的 using
using Package = Dictionary<string, int>;

// 定义一个委托类, 指明待委托方法的特征: 1. 返回值 (string) 2. 参数 (string, int, Package)
public delegate string DelegateBuy(string itemName, int num, Package package);

class Program
{
public static void Main()
{
// 实例化 玩家包裹
Package package = new Package();

// 实例化 委托
// 当 "待委托方法" 是静态方法时, 可以直接将静态方法作为参数传递.
DelegateBuy delegateBuy = Program.Buy;
DelegateBuy delegateBuy2 = new DelegateBuy(Program.Buy);

// 调用委托的写法和调用方法的写法一致
Console.WriteLine(delegateBuy("体力药水", 10, package));
Console.WriteLine(delegateBuy("体力药水", 25, package));
Console.WriteLine(delegateBuy2("魔力药水", 10, package));
Console.WriteLine(delegateBuy2("魔力药水", 5, package));

// 暂停
Console.ReadKey();
}

/// <summary>
/// 待委托的方法
/// </summary>
/// <param name="itemName">物品名称</param>
/// <param name="num">物品数量</param>
/// <param name="package">玩家包裹</param>
public static string Buy(string itemName, int num, Dictionary<string, int> package)
{
string str;

if (package.ContainsKey(itemName))
{
package[itemName] += num;
str = string.Format("您又购买了{0}个{1}!", num, itemName);
}
else
{
package.Add(itemName, num);
str = string.Format("您购买了{0}个{1}!", num, itemName);
}

return str;
}
}
}

Action 和 Func

在实例化委托的时候, 不仅仅可以使用自己定义的委托, 还可以使用 csharp 中已经定义好的两个泛型委托: Action<>Func<> 来实例化 (强烈建议使用).

Action

Action<> 待委托的方法都是没有返回值的方法, 参数使用泛型进行指定.

1
2
3
Action a; // 实例化出一个待委托方法特征为没有返回值, 没有参数的委托实例 a
Action<string> b; // 实例化出一个待委托方法特征为没有返回值, 有一个 string 类型参数的委托实例 b
Action<string, int[]> c; // 实例化出一个待委托方法特征为没有返回值, 具有一个 string, 一个 int 数组参数的委托实例 c

Func

Func<> 待委托的方法都是具有返回值的方法. 使用泛型来指定参数和返回值. 其中泛型中的最后一个类型为返回值的类型.

1
2
3
Func<string> a; // 实例化出一个待委托方法特征为没有参数, 返回值类型为 string 的委托实例 a
Func<string, int> b; // 实例化出一个待委托方法特征为有一个 string 类型的参数, 返回值为 int 类型的委托实例 b
Func<int, string, int[]> c; // 实例化出一个待委托方法特征为有两个 类型分别为 int 和 string 类型的参数, 返回值为 int 数组类型的委托实例 c

总结

  1. delegate 和 Delegate 的区别:

    Delegate 是类, delegate 是关键字.

  2. 定义委托

    不再自定义委托, 使用 Action<> 和 Func<>.

  3. 实例化委托

    使用泛型指明待委托方法的特征.

  4. Action 和 Func

    Action<> 指明无返回值, Func<> 最后一个泛型类型指明返回值.

  5. 委托的基本用法

    可以通过委托来调用方法.

参考链接

理解委托类型