C#学习笔记 委托

2022-05-05 18:37:56 浏览数 (1)

定义委托

有时候可能想要将一个方法传递给另一个方法。在C 中使用函数指针来实现,在JavaScript中由于函数也是对象所以直接可以在参数列表中传递。而在C#中需要使用委托。

要使用委托,首先需要定义它。

代码语言:javascript复制
//定义一个接受两个int参数,返回一个int值的委托
delegate int Operator(int x, int y);

定义之后就可以创建它。创建委托需要一个方法签名与定义委托时候一样的方法。在创建委托的时候还可以用简化的语法。

代码语言:javascript复制
//这是一个和委托匹配的方法
static int Add(int a, int b)
{
    return a   b;
}
//创建一个委托
Operator op = new Operator(Add);
//使用简化的语法创建委托
//Operator op = Add;

创建完成之后,就可以像使用一个普通方法一样使用委托了。

代码语言:javascript复制
Operator op = new Operator(Add);
Console.WriteLine(op(1, 2));

在后台,委托其实是一个类,这个类继承自System.MulticastDelegate,由编译器自动生成。

使用委托

这里通过将不同的方法传入计算函数,得出不同的计算结果。

代码语言:javascript复制
class DelegateSample
{
    //定义一个委托
    delegate int Operator(int x, int y);
    //定义一个和委托匹配的方法
    static int Add(int a, int b)
    {
        return a   b;
    }
    static int Sub(int a, int b)
    {
        return a - b;
    }
    //测试委托
    internal static void DelegateTest()
    {
        int a = 1, b = 2;
        DoCalc(a, b, Add);
        DoCalc(a, b, Sub);
    }
    //接受一个委托做参数的函数
    static void DoCalc(int a, int b, Operator op)
    {
        Console.WriteLine($"a和b运算的结果是:{op(a, b)}");
    }
}

预定义的委托

有些方法的签名比较常见,比如上面的int Add(int a,int b) 这样的。如果每次都手动创建一个新委托显得不那么方便。因此就有了Action< T>委托和Func< T>委托。

Action< T>委托表示一个void返回类型的方法。没有泛型参数的Action委托可以表示没有参数的方法,Action< in T>表示有一个参数的方法,Action< in T1,in T2>表示有两个参数的方法……最多可以有16个参数。

Func< T>委托表示有返回值的方法。和Action一样,Func< out TResult>表示没有参数、有返回值的方法,Func< in T,out TResult>表示有一个参数、有返回值的方法……最多可以有16个参数和一个返回值。

有这些泛型委托,很多时候就不需要自己手动定义泛型了。这样,上面的例子就可以修改一下。

代码语言:javascript复制
//使用Func<T>泛型委托
static void DoCalc(int a, int b, Func<int, int, int> op)
{
    Console.WriteLine($"a和b运算的结果是:{op(a, b)}");
}

多播委托

上面的委托都是一个委托只包含了一个方法,其实委托还可以包含多个方法。这时候称为多播委托。这时候,委托内部会保存一个迭代方法列表。

代码语言:javascript复制
class MultiDelegateSample
{
    //定义了三个方法,测试多播委托
    static void Method1()
    {
        Console.WriteLine("方法1:做了一些事情");
    }
    static void Method2()
    {
        Console.WriteLine("方法2:做了另一些事情");
    }
    static void Method3()
    {
        throw new Exception("方法3:抛出了一个异常");
    }
    internal static void MultiDelegateTest()
    {
        Action things = Method1;

        things  = Method2;
        things();

    }
}

需要注意的是,多播委托调用多个方法的顺序并不确定,所以避免编写依赖于特定调用顺序的代码。

如果多播委托中有一个方法抛出异常,那么整个方法调用链就会停止。这可能导致问题。例如上面的代码中包含了一个抛出异常的方法,如果将其加入到多播委托中,就会发现在所有方法执行完成前就会因为异常而终止。

代码语言:javascript复制
internal static void MultiDelegateTest()
{
    Action things = Method1;
    //会抛出异常的方法
    things  = Method3;
    things  = Method2;

    things();

}

为了避免这种情况,需要手动迭代方法列表。这样的话,整个多播委托就不会因为异常而意外终止了。

代码语言:javascript复制
internal static void MultiDelegateTest()
{
    Action things = Method1;
    things  = Method3;
    things  = Method2;

    Delegate[] methods = things.GetInvocationList();
    foreach (Action method in methods)
    {
        try
        {
            method();
        }
        catch (Exception e)
        {
            Console.WriteLine($"捕获到异常:{e.Message}");
        }
    }

}

0 人点赞