首页 > 编程笔记 > C#笔记 阅读:32

C#委托用法详解(附带实例)

C# 的委托是一种对方法的引用,类似于 C/C++ 的函数指针(function pointer),但是 C# 的委托与 C 的不同在于其面向对象,安全性较佳。所谓的委托从程序设计端的观点来看,就是将一个方法(method)或函数当作参数,传递给另一个方法。

简单地说,就是一个方法的参数是方法。

过去我们设计方法时,所传递的参数有 int、double、string 等对象。如果设计的方法比较复杂,就会传递多个不同类型的参数,这时在方法内会有 if 的条件判断语句,然后在不同的 if 条件区块内完成这些参数的工作。

C#委托操作

委托操作有 4 个步骤:

1) 声明委托

声明委托的关键词是 delegate,语法如下:
[存取修饰词] delegate 数据类型 委托名称(系列参数);

上述 [存取修饰词] 是可选项,其他是必需的,下列是声明委托 NumOperation() 的实例:
public delegate int NumOperation(int x, int y);
注意,委托声明必须在顶级语句后面,在顶级语句设计中来说委托一般都放在程序后方,但是在类声明之前。

2) 设计目标方法

可以创建目标方法,下列是实例:
public static int Add(int a, int b)
{
    return a + b;
}

public static int Sub(int a, int b)
{
    return a - b;
}
所设计的目标方法与委托必须符合下列条件:

3) 创建委托对象实例并设定目标方法的常见用法

我们可以使用 new 关键词创建委托对象,设定目标方法,例如,前面我们创建了两个目标方法,当我们创建委托对象时需要设定此对象的方法,下列是实例:
NumOperation opAdd = new NumOpertion(Add);
NumOperation opSub = new NumOpertion(Sub);
上述 opAdd 的目标方法是 Add,opSub 的目标方法是 Sub。当委托对象 opAdd 经过委托设定目标是 Add 方法后,这个 opAdd 对象就有和 Add 方法相同的能力。

4) 创建委托对象实例并设定目标方法的简化用法

还可以将创建委托对象实例简化如下:
NumOperation opAdd = Add;
NumOperation opSub = Sub;

有的程序设计师会分段撰写,先创建委托对象,再绑定方法实例化,如下:
NumOperation opAdd, opSub;
opAdd = Add;
opSub = Sub;

5) 调用委托

可以使用 Invoke() 或是直接使用 () 调用委托,下列是实例:
opAdd.Invoke(5, 2);
或是:
opAdd(5, 2);

6) 简单的委托实例

因为顶级语句必须在类型声明前方,所以委托是在程序下方声明。

【实例 1】简单委托的实例,笔者分别使用完整与简化声明委托目标对象,同时调用时也使用 Invoke() 和简化 () 方法。
static int Add(int a, int b)
{
    return a + b;
}

static int Sub(int a, int b)
{
    return a - b;
}

NumOperation opAdd = new NumOperation(Add);   // 创建对象与目标是 Add
NumOperation opSub = Sub;                     // 创建对象与目标是 Sub

// 使用委托对象调用方法
Console.WriteLine($"Delegate Add 结果:{opAdd.Invoke(5, 2)}");
Console.WriteLine($"Delegate Sub 结果:{opSub(5, 2)}");

delegate int NumOperation(int x, int y);       // 声明委托
执行结果为:

Delegate Add 结果: 7
Delegate Sub 结果: 3


读者可以留意第 11 行和第 12 行创建委托目标对象的方法:
【实例 2】重新设计实例 1 先声明委托对象,再绑定方法的写法。
static int Add(int a, int b)
{
    return a + b;
}

static int Sub(int a, int b)
{
    return a - b;
}

NumOperation opAdd, opSub;              // 创建委托对象 opAdd 和 opSub
opAdd = Add;                            // 设置 opAdd 委托目标方法是 Add
opSub = Sub;                            // 设置 opSub 委托目标方法是 Sub

// 使用委托对象调用方法
Console.WriteLine($"Delegate Add 结果:{opAdd.Invoke(5, 2)}");
Console.WriteLine($"Delegate Sub 结果:{opSub(5, 2)}");

delegate int NumOperation(int x, int y); // 声明委托
执行结果与实例 1 相同。上个程序的重点是第 11~13 行,在面向对象的概念中,委托的目标方法大多在类内声明,详情可以参考下列实例。

【实例 3】使用 MyDelegate 类创建方法,然后让顶级语句设定委托对象目标方法,再予以调用,这个程序多设计了回传静态变量的方法。
NumOperation opAdd = MyDelegate.Add;   // 创建对象与目标是 Add
NumOperation opSub = MyDelegate.Sub;   // 创建对象与目标是 Sub

// 使用委托对象调用方法
opAdd(5, 2);
Console.WriteLine($"Delegate Add 结果 {MyDelegate.getNum()}");
opSub(5, 2);
Console.WriteLine($"Delegate Sub 结果 {MyDelegate.getNum()}");

delegate int NumOperation(int x, int y);   // 声明委托

public class MyDelegate
{
    static int result = 0;                 // 静态变量

    public static int Add(int a, int b)    // 加法
    {
        result = a + b;
        return result;
    }

    public static int Sub(int a, int b)    // 减法
    {
        result = a - b;
        return result;
    }

    public static int getNum()             // 回传静态变量
    {
        return result;
    }
}
执行结果与实例 1 相同。

7) 调整委托指向

当委托声明后同时会绑定此委托的目标,也就是将委托目标封装在对象内,但是程序执行过程我们仍是可以将委托指向其他目标方法。

【实例 4】将委托指向其他方法。
SampleMethod sm = new SampleMethod();       // 创建类 SampleMethod 对象
MyDelegate obj = sm.InstanceMethod;         // 目标是 InstanceMethod 方法的委托
obj();                                      // 执行 obj()

obj = SampleMethod.StaticMethod;            // 将委托指向 StaticMethod 方法
obj();                                      // 执行 obj()

delegate void MyDelegate();                 // 声明委托
public class SampleMethod
{
    public void InstanceMethod()
    {
        Console.WriteLine("我是 InstanceMethod 方法");
    }
    public static void StaticMethod()
    {
        Console.WriteLine("我是 StaticMethod 方法");
    }
}
执行结果为:

我是 InstanceMethod 方法
我是 StaticMethod 方法

上述程序第 1 行先创建类对象,第 2 行创建委托对象 obj,同时委托目标指向 InstanceMethod(),这是非静态方法,需使用类对象 sm 才可以委托,程序第 3 行执行委托obj(),可以得到第 1 行的输出。

程序第 5 行将委托指向 SampleMethod 类的静态方法 StaticMethod(),程序第 6 行执行委托 obj(),可以得到第 2 行的输出。

C#委托当作方法的参数

委托也可以被当作方法的参数,详情可以参考下列实例。

【实例 5】委托当作方法的参数。
static void InvokeDelegate(MyDelegate delegateObj)
{
    delegateObj("认识 C# delegate 委托对象当作方法的参数");
}

MyDelegate objA = JobA.MethodA;     // 创建委托当作与目标
InvokeDelegate(objA);               // 调用 InvokeDelegate() 方法
MyDelegate objB = JobB.MethodB;     // 创建委托当作与目标
InvokeDelegate(objB);               // 调用 InvokeDelegate() 方法

public delegate void MyDelegate(string message);  // 声明委托
public class JobA                               // 类 JobA
{
    public static void MethodA(string msg)
    {
        Console.WriteLine($"调用 MethodA :{msg}");
    }
}
public class JobB                               // 类 JobB
{
    public static void MethodB(string msg)
    {
        Console.WriteLine($"调用 MethodB :{msg}");
    }
}
执行结果为:

调用 MethodA :认识 C# delegate 委托对象当作方法的参数
调用 MethodB :认识 C# delegate 委托对象当作方法的参数

上述程序第 6 行声明委托对象 objA 时,就已经将 JobA.MethodA 的方法封装在此 objA 对象内了。同样第 8 行声明委托对象 objB 时,就已经将 JobB.MethodB 的方法封装在此 objB 对象内了。所以第 7 行和第 9 行对象 objA 和 objB 分别调用 InvokeDelegate(),可以得到不同的内容。

C#多播委托

委托可以指向多个方法,这个概念称为多播委托(multicast delegate),在这个概念下可以用“+”或是“+=”将方法加到调用列表中,使用“-”或是“-=”则是将特定方法删除。

【实例 6】使用两个委托,然后用“+”创建多播委托,然后用“-”移除多播委托中的一个委托,分别调用这些委托同时观察执行结果。
MultiDelegate hiDel, byeDel, multiDel, subHiDel;

hiDel = Greeting.Hi;              // 设置委托 hiDel 的引用方法为 Hi
byeDel = Greeting.Goodbye;        // 设置委托 byeDel 的引用方法为 Goodbye

// 两个委托相加,创建多播委托(Multicast Delegate)
multiDel = hiDel + byeDel;
// 从多播委托(Multicast Delegate)中移除 hiDel 委托
subHiDel = multiDel - hiDel;

Console.WriteLine("启动 delegate hiDel:");
hiDel("Person A");

Console.WriteLine("启动 delegate byeDel:");
byeDel("Person B");

Console.WriteLine("启动 delegate multiDel:");
multiDel("Person C");

Console.WriteLine("启动 delegate subHiDel:");
subHiDel("Person D");

delegate void MultiDelegate(string s);     // 声明委托

public class Greeting
{
    // Define two methods that have the same signature as CustomDel.
    public static void Hi(string name)     // 定义 Hi() 方法
    {
        Console.WriteLine($" 早安, {name}!");
    }

    public static void Goodbye(string name)    // 定义 Goodbye() 方法
    {
        Console.WriteLine($" 再见, {name}!");
    }
}
执行结果为:

启动 delegate hiDel:
  早安, Person A!
启动 delegate byeDel:
  再见, Person B!
启动 delegate multiDel:
  早安, Person C!
  再见, Person C!
启动 delegate subHiDel:
  再见, Person D!

C#泛型委托

在 C# 程序中,也可以将泛型应用到委托中,定义泛型委托(generic delegate),这时设定委托对象的目标方法时需指定数据类型。

【实例 7】声明泛型委托,然后分别使用 int 和 string 数据类型实体化此泛型委托对象。
Add<int> sum = MyAdd.Sum;       // 声明泛型委托对象和目标方法
Console.WriteLine(sum(3, 6));

Add<string> cat = MyAdd.Concat; // 声明泛型委托对象和目标方法
Console.WriteLine(cat("C# ", "是面向对象程序语言"));

public delegate T Add<T>(T x, T y);  // 声明泛型委托 Add

public class MyAdd
{
    // 参数是 int
    public static int Sum(int a, int b)
    {
        return a + b;
    }

    // 参数是 string
    public static string Concat(string str1, string str2)
    {
        return str1 + str2;
    }
}
执行结果为:

9
C# 是面向对象程序语言

相关文章