云计算、AI、云原生、大数据等一站式技术学习平台

网站首页 > 教程文章 正文

C#之委托

jxf315 2024-12-04 12:13:14 教程文章 51 ℃

委托

当要把方法当成参数传给其他方法时,就用到了委托。我们很多时候都是将一般的数据作为参数传递,但将方法作为参数传递可能你会觉得很少出现,其实不然。比如线程这一块,我们要提供启动线程的方法,Thread的构造函数的一个参数就是定义了线程要调用的方法。再比如,事件这里,可视化图形界面开发中很多时候就是处理事件,触发事件时,我们需要知道执行哪一个方法,这时就把要处理事件的方法作为参数传递给委托。我们可以将委托看成是一个特殊类型的对象,或者将委托看成是为方法的签名和返回值类型指定了一个名称,这样就非常好理解了。

要使用委托,首先应该先声明一个委托,而你声明的这种类型的委托就表示可以接受哪种类型的方法,所以委托的类型安全性非常高,之后再创建这个委托的一个或多个实例。

delegate List<int> SelectData(List<int> allData, int num);

上面声明了一个委托,这个委托所指定所接受的方法带有一个List<int>类型的参数和一个int类型的参数,并且返回值类型为List<int>。

其实委托的定义特别像普通方法的定义,只是没有主体并用delegate修饰了。

委托的协变和逆变

协变可以理解为具体的输出,逆变可以理解为更加宽泛的输入。

    public delegate object DelDemo(FileStream fs);
    class Program
    {
        static void Main(string[] args)
        {
            // 可以指向
            DelDemo del = MethodDemo;
        }

        public static string MethodDemo(Stream s)
        {
            return "hello";
        }
    }

上面代码中,输入变量为FileStream,输出为object,因为协变,可以返回一个具体的类型string,因为逆变,可以传入比较宽泛的Stream类型。

Action<T>和Func<T>委托

除了可以自己定义新的委托类型之外,还可以使用Action<T>和Func<T>委托。

Action<T>泛型委托表示可以引用返回值类型为void的方法,而传递的参数类型可以自己定义。没有参数的方法的话直接用非泛型版本Action即可。Action委托其实就是用来保存没有返回值的方法的一个委托,参数比较随便。

例如:需要保存一个有string类型参数无返回值的一个方法

Action<string> action1 = m => { Console.WriteLine(m) ; }; 
action("hello");

需要保存两个参数无返回值的一个方法

Action<int, int> action1 = (x, y) => { Console.WriteLine(x + y); };
action1(10, 20);

Func<T>委托只有泛型版本,Func委托其实就是用来保存一定有返回值的方法的一个委托,参数有没有无所谓。

例如:

//假设有一个M2方法
public int M2(int n1, int n2, int n3)
{
	return n1 + n2 + n3;
}

// 前面三个是参数类型,最后一个是返回值类型
// 如果是Func<int>则表示没有参数,返回值类型是int的一个委托
Func<int, int, int, int> fun = M2;
fun(10, 20, 30);

多播委托

委托不仅只可以包含一个方法的调用,还可以包含多个方法,就是多播委托。调用多播委托,就按顺序连续调用多个方法,如果返回值类型不是void,就只能收到最后一个含有返回值委托的结果。

    class Program
    {
        static void Main(string[] args)
        {
            Action action = M1;
            action += M2;
            action += M3;
            action -= M2;   // 减少方法,最后只输出m1 m3
            action();

            Console.ReadKey();
        }

        static void M1()
        {
            Console.WriteLine("m1");
        }
        static void M2()
        {
            Console.WriteLine("m2");
        }

        static void M3()
        {
            Console.WriteLine("m3");
        }
    }

如果某个方法出现异常,之后的方法都不会执行了。为了避免这个问题,可以自己迭代方法列表。通过Get InvocationList()返回一个Delegate对象数组,遍历这个数组时try-catch调用的方法,即使捕获了异常也可以继续下一次迭代。

Tags:

最近发表
标签列表