C#——委托与事件系列一

本来应该学习泛型与委托的,但是发现C#这里还没有系统的记录过委托与事件,所以先打算把委托与事件补上再继续泛型与委托的记录。
然后呢,今天如果没有意外的话unity方面也会记录一些关于编辑器扩展的笔记,还有一直想学习的C#socket(套接字)的笔记。
万事开头难,只要开了头,剩下的事就可以继续下去了。
关于委托与事件,前面其实也有记录到,但是那是关于在unity中使用委托与事件的一个小事例而已,这里主要是记录一下C#中关于委托与事件到底是讲的什么?如何使用?以及有什么坑需要注意。
我打算把这个写成一个系列,毕竟我也是边学边写,然后呢,后续也会再次写到如何将委托与事件运用到unity中。
还有就是,还会重新再学一下C#的设计模式,基础一定要打牢固。

OK,废话说完,开始进入正题:
首先什么是委托?委托,delegate,是表示对具有特定参数列表和返回类型的方法的引用的类型。 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 你可以通过委托实例调用方法。(MSDN解释)
MSDN给出的解释说实话,很恶心,还绕口。
其实,在我看来,委托就是一种自定义的类型,就类似于一个class,然后我们可以通过这个类型来创建实例对象,这个实例对象,其实类似于引用类型的对象那样,存储的是一个“地址”,只不过这个地址指向的是函数方法,也就是说,委托实例就类似于C++中的函数指针那样。
委托是安全封装方法的类型,类似于C和C++中的函数指针,委托是面向对象的。类型安全的和可靠的。
总结: 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递。
可能解释的还是很乱,不过没关系,有个了解,不需要死扣定义。

继续,来看一下委托的定义:public delegate void MyDelegate(string str);
注意,从这儿定义来看,其实委托的定义与一般方法的定义,只是多了一个delegate关键字而已。
解释一下整个定义的意思:
public就是访问权限,没有什么差别。
delegate 声明委托的关键字。
void,方法的返回值,如果你将某个方法绑定到委托上,那么该方法的返回值要与委托定义时的一致,在这里了都要是void。
MyDelegate,委托的名字,其实就是一个类名,我们可以使用它来创建我们的委托对象。(string str),方法参数,如果你将某个方法绑定到委托上,那么该方法的参数要与委托定义时的一致,在这里参数是一个string类型。
当我们声明一个委托后,在编译时,会自动生成一个委托类型,自动继承MulticastDelegate,而MulticastDelegate又是继承自Delegate类的。
注意:这里,我们声明的是一个委托,虽然看起来像是声明了一个方法,它没有具体的实现,因为它相当于一个类。

再次继续,如何使用委托?(先不要着急事件)
直接来看例子好了:
首先我在一个类中写了两个方法,一个实例方法,一个静态方法:
public class TestMethod{
public static void StaticMethod(string str)
{
Console.WriteLine("StaticMethod:"+str);
}
public void InstantMethod(String str)
{
Console.WriteLine("InstantMethod:"+str);
} }
然后在program类中使用这个类:

class Program
 {
     static void Main(string[] args){
         MyDelegate myDel;
         TestMethod testMethod = new TestMethod();
         myDel=testMethod.InstantMethod;
         myDel+=TestMethod.StaticMethod;
         myDel("AAA");
         myDel-=testMethod.InstantMethod;
         myDel("BBB");
     }
 public delegate void MyDelegate(string str);
 
 public class TestMethod
 {
    public static void StaticMethod(string str)
    {
        Console.WriteLine("StaticMethod:"+str);
    }

    public void InstantMethod(String str)
    {
          Console.WriteLine("InstantMethod:"+str);
    }
 }

}

结果会是什么?输出:
C#——委托与事件系列一_第1张图片
图片

OK,用这个例子来解释一下,如何使用委托:
首先在Main()函数中,我们创建了一个委托对象 myDel,以及一个TestMethod类的对象 testMethod,
然后我们将testMethod的一个实例方法 InstanrMethod用“=”“赋值”给了myDel,有没有很熟悉的样子?
其实类似于,int i;i=1;这个样子的赋值,委托就是方法的引用类型,其对象的值就是一个方法名。前提是,该方法与委托的定义一致。
然后又有两个运算符,“+=”和“-=”,委托允许向其注册方法,那么也就意味着,可以注销方法,这就是observe模式的基础。

对于委托可以有多个方法,我们称之为多播。

而且从这个例子可以看出:使用委托对象可以封装实例方法和静态方法。

其实,在定义委托的时候,我们还能这样,如下图:
C#——委托与事件系列一_第2张图片
图片

但是,注意,如果我们直接:MyDelegate myDel = new MyDelegate();

这是会报错的,如下图:

C#——委托与事件系列一_第3张图片
2.png

是不是会想到构造函数,这个问题的解决方式,我是采用了,将委托的定义在封装一次,如下所示:

namespace class1
{
    class Program
    {
        static void Main(string[] args)
        {
            Method mt = new Method();
            Del del = new Del();
            del.myDel = mt.InstantMethod;
            del.myDel += Method.StaticMethod;
            Console.ReadKey();
        }
    }
    class Del
    {
        public MyDelegate myDel;

    }
    public delegate void MyDelegate(string str);

    class Method
    {
        public void InstantMethod(string str)
        {
            Console.WriteLine("InstantMethod" + str);
        }
        public static void StaticMethod(string str)
        {
            Console.WriteLine("StaticMethod" + str);
        }
    }
}

怎么说呢,就类似于一种曲线救国的样子,既然我不能new出来你,那么我就new出来封装你的类。然后用这个类的对象来调用你,大概就是这个意思。
甚至,我们可以对委托进行“加减运算”以及判断是否相等的逻辑判断(PS:其实就是增加方法与删除方法)
比如,我们来改一下Main函数里的代码:

C#——委托与事件系列一_第4张图片
3.png

我们来看看结果:

C#——委托与事件系列一_第5张图片
5.png

看到了没有,可以进行逻辑判断,当然,你可以自己去尝试一下减法的作用。

OK,关于委托的最最基础的知识点先整理到这里,还有一些关于匿名方法和Lambda表达式运用在委托中,会在下篇中记录。

那么现在开始记录一下关于事件的基础知识:
什么是事件呢?
老规矩,来看一下MSDN上的解释:
类或对象可以通过事件向其他类或对象通知发生的相关事情,发送事件的类称为“发行者”,接收事件的类称为“订户”。
解释的还算是清晰吧,不过可能对于一些新人来说还是一头雾水的样子。

那么开始事件的记录:我先说一句:事件是对委托的隐藏。(老规矩,不要试图去理解它,有个印象就可以了).
先来说一下,为什么需要事件?
面向对象三大特性:继承,封装,多态。其中封装就是指将某些我们不想让外部看到的东西包围起来,给外部留出来一个接口,可以使用就行,不让外部的人员对其进行改动,这是做使得代码更加的安全。
我们现在都知道了,委托其实就是一个类,那么对于其对象而言,访问权限肯定是该public的时候public,该private的时候private。
但是,当private的时候,我们就在客户端无法调用它了,也就是说它就没什么卵用了,那我们干嘛还声明它出来。
但是,当public的时候,又严重的破坏了封装性,我们可以随意的修改,这点后果也很严重。
如果是针对别的类型,比如string,int等等,那么我们可能就会想到用属性来对其进行封装。
于是,Event出场了,它封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。
OK,来看一下事件的定义:我们将委托去事件放到同一个类中,并且给一个触发方法:

    class EventTest
    {
        public delegate void MethodHandler(string str);
        public event MethodHandler MethodEvent;
        
        public void Print(string str)
        {
            if (MethodEvent != null)
            {
                MethodEvent(str);
            }
        }
    }

看到了没有,public event MethodHandler MethodEvent;
这个事件的定义,其实就是比创建委托对象多了一个关键字event。
然后在Main函数中,我们这样写:

           EventTest ev = new EventTest();
            Method mt = new Method();
            ev.MethodEvent += mt.method;
            ev.MethodEvent += mt.InstantMethod;
            ev.MethodEvent += Method.StaticMethod;
            ev.Print("ppppppppppppppppp");
            Console.ReadKey();

这个时候,你再去用“=”就没有卵用了。
先看一下运行结果:

C#——委托与事件系列一_第6张图片
10.png

当触发事件的方法发生的时候,订阅该事件的方法都会按照订阅顺序进行执行动作。
其实,这已经算是一个很简版很简版的observe模式的例子了。
还有就是,有时候我们会遇到带有EventArgs参数的事件模型,这个是干什么用的呢?
其实这个是.Net Framework中的委托与事件,其有一个编码规范。
我们先看一下编码规范,下一篇再讲如何编写这样的委托与事件。
编码规则:

  • 委托类型的名称都应该以EventHandler结束。
  • 委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型(就是监视对象),一个 EventArgs类型(或继承自EventArgs)(其存储了观察者感兴趣的数据,也就是事件触发的关键点)。
  • 事件的命名为 委托去掉 EventHandler之后剩余的部分。
  • 继承自EventArgs的类型应该以EventArgs结尾。

系列一就先记录到这里。

你可能感兴趣的:(C#——委托与事件系列一)