C#—委托与事件

371 阅读6分钟

委托

在C#中,委托从字面上理解就是一种代理。

作用:将方法当作参数进行传递调用。除此之外委托还可以同时注册多个方法,当调用委托是等于同时调用了注册在这个委托上的全部方法。

目的:解耦各个模块,避免直接调用其他模块的方法,通过注册和移除注册的方式来控制其他模块方法的调度。

C#中委托有三种: delegate、Action、Func

接下来讲一讲三种委托的用法和特点:

1.delegate

先说明如何定义和声明委托:

//这是定义了一个委托类型,并不是委托实体,本质上是一个类
delegate void Mydelegate();
    

需要注意的是,在定义一个委托类型时候需要像声明一个方法一样需要定义返回值类型和参数类型,参数个数。如上面的Mydelegate这个委托类型,返回值为void,括号里为空所以没有参数。

//利用类来声明一个对象,这里的类型是上述的Mydelegate类型,声明的对象就是testDelegate
MyDelegate testDelegate;
    

接下来说明如何使用委托,假设现在我们有一个这样的方法:

void Test()
{
    Consolo.WriteLine("这里是Test");

}

委托可以通过 = ,+=来进行注册(绑定方法),注册的方式有两种:

 //1.通过" = "来进行注册
 testDelegate = Test;
 
 //2.通过"+="来进行注册
 testDelegate += Test;

两种注册方式的区别:

第一种方式一般用于第一次给委托进行注册,就好像赋值运算符的用法一样,将一个方法赋给了委托。需要注意的是,正如刚刚说的,像赋值运算符一样,所以第二次用赋值运算符注册方法时,会覆盖掉之前的注册。如:

testDelegate = Test();
testDelegate();
testDelegate = Test1();

上述代码的运行结果:

这里是Test
这里是Test1

原先注册的 Test() 被后面注册的 Test1() 覆盖了。

第二种注册方式不仅可以用在第一次给委托进行注册,也可以对委托进行多次注册相同或者不同的方法,如:

testDelegate+=Test();
testDelegate+=Test();
testDelegate+=Test1();
testDelegate+=Test2();
testDelegate();

运行结果:

这里是Test
这里是Test
这里是Test1
这里是Test2

当调用一次委托时等于同时调用全部注册在该委托上的方法

如果想移除注册,则通过"-="来进行移除注册的操作,如:

testDelegate+=Test();
testDelegate+=Test();
testDelegate+=Test1();
testDelegate+=Test2();
testDelegate();
//这里与上面的代码是一样的
Console.WriteLine("------------------");
testDelegate-=Test()
testDelegate-=Test1()
testDelegate();

运行结果:

这里是Test
这里是Test
这里是Test1
这里是Test2
------------------
这里是Test
这里是Test2

需要注意的是,在给委托注册的时候,委托和被注册的方法需要在返回值和参数数量,类型顺序,保持一致。

如何通过委托将方法作为参数给其他方法进行调用?请看下面的代码:

    //这是定义了一个MyDelegate类
    delegate void MyDelegate();

    //利用MyDelegate类来声明一个对象
    static MyDelegate delegatel;

    static void Main(string[] args)
    {
    
        //将委托作为参数传入Test2中
        Test2(delegatel);
        Console.WriteLine("--------------");

        delegatel = Test1;
        Test2(delegatel);
    }

    static void Test1()
    {
        Console.WriteLine("Test1");
    }
    
    //参数类型为MyDelegate
    static void Test2(MyDelegate function)
    {
        Console.WriteLine("这里是Test2");
        //调用function,如果function上已经注册过方法了,那么可以调用
        if (function != null)
        {
            function();
        }
    }

代码结果:

这里是Test2
--------------
这里是Test2
Test1

可以看到,Test2方法的参数为一个MyDelegate类型的委托,然后就像调用普通的方法一样来调用function。而在Main方法中,调用Test2时,需要将delegatel作为参数。

需注意以下情况:

//这是两个不同的类型
delegate void MyDelegate();
delegate void MyDelegate1();

//分别声明一个对象
static MyDelegate delegatel;
static MyDelegate1 delegate2;

//在上面的代码中这个是成立的,因为Test2方法的参数类型为MyDelegate。
Test2(delegatel);

//这是不成立的,因为delegate2MyDelegate1,而Test2方法的参数类型为MyDelegate。
Test2(delegate2);

2.Action

Action与上面的delegate在操作上区别不大,都可以通过 = ,+= 来注册方法。通过 -= 来移除注册。

区别有以下几点:

  1. 用Action声明的就是一个对象,而不是一个委托类型,不需要在进行一次声明 如:

    Action mAction;//mAction是一个委托实体了,可以用mAction进行注册和移除注册的操作了

2.声明上的细节,delegate在类型的定义时说明返回值类型和参数,在声明一个实体对象时是看不出来的,如:

delegate void Mydelegate();//这是一个返回值为void,无参数的delegate委托类型的声明
Mydelegate testDelegate;

而,Action在委托对对象的声明时说明参数类型,如:

Action mAction;//这是一个无参数的(返回值为void)Action委托
Action<int, int> mAction1;//这是带两个int类型参数的Action委托

3.Action没有返回值,而delegate可以有返回值,也可以没有返回值。

3.Func

Func与Action在操作上没什么区别,都可以通过 = ,+= 来注册方法。通过 -= 来移除注册。

最主要的区别就是,Func有返回值,Action没有返回值。这种区别直接体现在对委托的声明上面,如:

Action<int, int> mAction;//这是带两个int类型参数的Action委托
Func<int,int> mFunc;//这是带一个int类型参数的Func委托,返回值为int类型。

虽然形式上是写Func<int,int>。但是,最后的int并不代表一个参数,而是表示返回值类型。也就是说,Func的尖括号里面最少要填一个类型。例:

Func<void> testFunc;//这种情况是不允许的,因为Func有返回值。
Func<int> testFunc1;//这个是可以的,这表示返回值为int,没有参数
Func<float,int> testFunc2;//这表示有一个参数,类型为float,返回值类型为int

事件

event用于修饰委托,也就是上面提到的delegate、Action、Func。被event修饰后的委托,我们可以它为叫事件。

作用:事件只能在类内进行调用,在类外面不可以调用,但是可以在类的外面进行注册和移除注册。需要注意的是,注册时只能用 +=,不可以使用 = 进行注册。如:

class TestClass
    {
        public event Action mAction;

        public void Callback()
        {
            mAction();
        }
    }


    static int Main()
    {

        TestClass testClass = new TestClass();

       // testClass.mAction = Test();//不可以使用 = ,对事件进行注册

        testClass.mAction += Test;//通过+=对事件进行注册

        //testClass.mAction();//不可以在类的外面调用

        testClass.Callback();//通过类内的其他函数进行调用


        return 0;
    }
    static void Test()
    {
        Console.WriteLine("这里是Test");
    }

运行结果:

这里是Test()

最后

delegate、Action、Func

delegateActionFunc
返回值可以有,可以没有没有

注:本文纯属个人观点,仅供参考,如有错误和遗漏欢迎指正。