Unity C#委托与事件详解


在面试中,委托与事件几乎是必问的。如果面试官和面试者互不相识,面试官不问委托,那我严重怀疑这家公司的技术水平,这个可以说是C#入门与掌握的分水岭之一,非常重要。所以,这里我整理一片笔记,对委托和事件做一个全面的整理和归纳。这里重在理解,只有理解了才能真正掌握它。


1. 为什么C#会有委托事件?
解决观察者模式的缺陷。这里又引申出一个问题,什么是观察者模式,观察者模式的缺陷又是什么?在回答这个问题前,我们其实已经知道一个答案,那就是先有设计模式中的观察者模式,再有委托事件这一技术。它有一个前后顺序,如果你要彻底学会委托事件,那么就要先学会观察者模式,还要了解观察者模式的缺陷。

2.观察者模式的缺陷:
观察者模式是对主题对象和观察者对象进行解耦,使双方都依赖与抽象,而不是依赖于对方的具体对象,使双方的变化都不会影响到对方的具体对象。当多个对象需要根据一个对象的状态发生相应的改变或操作时,可使用观察者模式。

这里我写一个小demo,非常简洁。还是猫和老鼠,猫是主题对象,人和老鼠是观察者。猫叫的时候,人和老鼠要执行相应操作。

使用观察者模式实现这一功能:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObserverCatDemo : MonoBehaviour
{
    void Start()
    {
        CatSubject cat = new CatSubject();
        Mouse mouse = new Mouse();
        Person person = new Person();
        cat.Add(mouse);
        cat.Add(person);
        cat.Call();
    }
}
public abstract class Subjectss
{
    public abstract void Add(Observerss observer);
    public abstract void Remove(Observerss observer);
    public abstract void Call();

}
public abstract class Observerss
{
    public abstract void Update();
}
public class CatSubject : Subjectss
{
    public IList<Observerss> Observers = new List<Observerss>();
    public override void Add(Observerss observer)
    {
        Observers.Add(observer);
    }
    public override void Remove(Observerss observer)
    {
        Observers.Remove(observer);
    }
    public override void Call()
    {
        foreach (Observerss observer in Observers)
        {
            observer.Update();
        }
    }
}
public class Mouse : Observerss
{
    public override void Update()
    {
        Debug.Log("猫叫了,我得跑了");
    }
}
public class Person : Observerss
{
    public override void Update()
    {
        Debug.Log("猫叫了,应该是饿了");
    }
}

然后,我们使用委托来实现这一功能

using System;
using System.Collections.Generic;
using UnityEngine;

public class DelegateCat:MonoBehaviour
{
    private void Start()
    {
        DeCat deCat = new DeCat();
        DePerson person = new DePerson();
        DeMouse mouse = new DeMouse();
        deCat.call += person.Notify;
        deCat.call += mouse.Run;
        deCat.call();
    }
}
public delegate void CallEvent();//声明委托类型
public class DeCat
{
    public CallEvent call;

}
public class DePerson
{
    public void Notify()
    {
        Debug.Log("猫叫了,应该是饿了");
    }
}
public class DeMouse
{
    public void Run()
    {
        Debug.Log("猫叫了,我得跑了!");
    }
}


我们做一下对比:
1.发现实现同样的功能,使用委托非常的简洁,并且主题猫的类里没有任何观察者类成员,两者是完全独立的。而观察者模式双方并没有完全的独立,抽象主题通知时依然依赖于抽象观察者。

2.观察者不需要继承观察者接口,它的通知方法名可以不同,比如说人的执行方法是Notify(),猫的执行方法是Run(),现实中本就是这样的,方法名本就不一定相同。

也就是说观察者模式的缺陷为:

1.抽象主题依旧依赖于抽象观察者。

2.具体的观察者,通知方法被固定了。

观察者模式存在的意义就是解耦,它使观察者和被观察者的逻辑不再搅在一起。而是彼此独立,互不依赖。而使用委托则能使得观察者和主题完全解耦,甚至不需要知道对方的存在。

3.为什么要费这么大劲?
设计的核心思想:尽可能减少耦合,如果发现代码耦合,就要采取解耦技术,让数据模型,业务逻辑和视图显示三层之间彼此降低耦合,把关联依赖降到最低,而不至于牵一发而动全身。原则就是A功能的代码不要写在B功能代码中,如果两者之间需要交互,可以通过接口,通过消息,甚至是引入框架,但总之就是不要直接交叉写。

在软件工程中,模块的内聚和耦合是度量模块化质量的之一。

高内聚:是指模块是由相关性很强的代码组成,只负责一项任务,也就是单一职责原则,这样的模块,无论是从设计,实现,还是阅读,都能体现出保持专一性带来的好处。

低耦合:是指模块之间尽可能的使其独立存在,模块之间不产生联系不可能,但模块与模块之间的接口应该尽量少而简单。

耦合:在软件工程中,对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护成本越高,因此对象的设计应该使类和构建之间的耦合最小。

解耦:字面意思,就是解除耦合关系,在软件工程中,降低耦合度即可以理解为解耦,模块间有依赖关系,必然存在耦合,理论上的绝对零耦合是做不到,但可以通过一些现有的方法将耦合度降至最低。

4.委托?事件?封装!
这里我们只要对委托的例子做一个小小的变动

public class DeCat
{
    public event CallEvent call;

}


没错我们给声明前面加个event就可以了,但是你突然发现,编译不过去,有报错。这里也是事件与委托核心的区别。
事件是类或对象向其他类或对象通知发生的一种特殊签名的委托。它的本意是对委托的封装,它在外部只能被订阅或取消订阅,但是不能发布。一句话总结:事件只能在外部被订阅,但是不能在外部被触发。只能通过内部的公开方法,在方法内部触发事件,这样可以使得程序更加的安全。

这里我通过的例子来说明:当使用委托变量时,客户端可以直接通过委托变量触发事件,也就是直接调用
deCat.call();这将会影响到所有注册了该委托的变量。而事件的本意应该为在事件发布者在其本身的某个行为中触发,不如说在方法CallMethod()中满足某个条件后触发。通过添加event关键字来发布事件,事件发布者的封装性会更好,事件仅仅是供其他类型订阅,而客户端不能直接触发事件(语句deCat.call()无法通过编译),事件只能通过事件发布者DeCat类的内部触发(比如在方法deCat.CallMethod()中),换言之,就是call()语句只能在deCat内部被调用。这样才是事件的本意,事件发布者的封装才会更号。
以下是完整代码

using System;
using System.Collections.Generic;
using UnityEngine;

public class DelegateCat:MonoBehaviour
{
    private void Start()
    {
        DeCat deCat = new DeCat();
        DePerson person = new DePerson();
        DeMouse mouse = new DeMouse();
        deCat.call += person.Notify;
        deCat.call += mouse.Run;
        //deCat.call();//编译无法通过
        deCat.CallMethod();
    }
}
public delegate void CallEvent();//声明委托类型
public class DeCat
{
    public event CallEvent call;
    public void CallMethod()
    {
        call();
    }

}
public class DePerson
{
    public void Notify()
    {
        Debug.Log("猫叫了,应该是饿了");
    }
}
public class DeMouse
{
    public void Run()
    {
        Debug.Log("猫叫了,我得跑了!");
    }
}


5.是不是还少了点什么?
关于委托事件的用法,网上搜一大堆,要多详细有多详细,我这里就没必要赘述了。


————————————————
参考链接:

https://blog.csdn.net/q493201681/article/details/82352616

 https://www.cnblogs.com/kissazi2/p/3189685.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值