Delegate 你会用吗?

本文详细介绍了C#中委托的基本概念及使用方法,包括不同版本C#中引入的匿名方法和Lambda表达式,并通过实例展示了协变与逆变的应用场景。

Delegate的相关概念不清楚的请点击delegate 概念 ,以下只讲解Delegate的几种使用方式,delegate在C# 2.0 版引入了匿名方法,3.0 引入了 Lambda 表达式,匿名方法和Lambda表达式(在某些上下文中)都可编译为委托类型。这些功能统称为匿名函数。有关 Lambda 表达式的更多信息请点击匿名函数 .委托的三种使用方法例子如下:

public Test{
    delegate void myDelegate(string s,int i);//声明委托


    public static void Print(string s,int i){
        Console.writeLine(s+i);
    }
    myDelegate d1=new myDelegate(Print);//1.0方式


    myDelegate d2=delegate(string s,int i){Console.WriteLine(s+i);};//2.0匿名方式


    myDelegate d3=(x,num)=>{Console.WriteLine(x+num);};//3.0 Lambda 表达式方式
   
    d1("传统方式",1); d2("匿名方式",2);d3("Lambda表达式方式",3);
}

在.netFramework3.5中委托方法不必与委托签名完全匹配,具体请点击 委托中的协变与逆变 以下是实例:

1>:协变 :用于继承
class Mammals{}//父类
class Dogs : Mammals{}//子类
class Program
{
    public delegate Mammals HandlerMethod();// 定义委托
    public static Mammals FirstHandler()//返回值为父类
    { return null; }
    public static Dogs SecondHandler()//返回值为子类
    { return null;}
    static void Main()
    {
        HandlerMethod handler1 = FirstHandler;
        HandlerMethod handler2 = SecondHandler;
    }
}
2>:逆变:以前必须使用若干个具有相同参数的事件处理程序,现在只需要一个
System.DateTime lastActivity;
public Form1()
{
    InitializeComponent();
    lastActivity = new System.DateTime();
    this.textBox1.KeyDown += this.MultiHandler; //文本框的KeyDown事件
    this.button1.MouseClick += this.MultiHandler; //按钮的MouseClick事件
}
//事件处理程序
private void MultiHandler(object sender, System.EventArgs e)
{lastActivity = System.DateTime.Now;}


在.netFramework中System.Action委托经典使用方法:

使用Action<T>输出泛型List中的值
  public void actionTest(){//测试方法
        List<string> l = new List<string>();
        l.Add("dd");
        l.Add("bn");
        l.Add("cn");
        //1:可以使用外部委托方法(方法名做为参数)
        l.ForEach(Print);

        //2:可以使用委托类型(匿名委托)
        l.ForEach(delegate(string name)
        {
            this.TextBox1.Text += name;
        });
    }
    private void Print(string s) {//输出方法
        this.TextBox1.Text += s;
    }

使用委托方式调用数组列表中静态声明的方法
    //测试入口
        public void ActionTest()
        {
            string[] ss = { "method1", "method2" };
            Action<string[]> a = (s) => Actions(ss);//Lambda
        }
        delegate void MyDelegateMethod();//定义委托
        public void Actions(string[] s)
        {
            for (int i = 0; i < s.Length; i++)
            {
                System.Reflection.MethodInfo d = this.GetType().GetMethod(s[i]);//获取实例方法
                Delegate de = MyDelegateMethod.CreateDelegate(typeof(MyDelegateMethod), d);//创建委托类型方法
                if (de != null)
                {
                    MyDelegateMethod m = (MyDelegateMethod)de;//类型转换
                    m();//调用方法
                }
            }
        }
        public void method1() {
            Console.WriteLine("method1");
        }
        public void mehtod2() {
            Console.WriteLine("method2");
        }

希望通过以上几个小实例,能让你加深对delegat印象。如果有什么问题,请指证,多谢!

 

 

 

<think> 首先,用户的问题是:“我想了解Unity中event和delegate的区别 请问Unity中event和delegate的区别”。系统指令要求我使用中文回答,确保回答结构清晰,帮助用户逐步解决问题,并尽量保证真实可靠。 我手头有三个引用: - 引用[1]:展示了UnityEvent的使用示例。 - 引用[2]:讨论了Unity中事件(Event)与委托(Delegate)的关键差异。 - 引用[3]:解释了Unity C# 委托delegate和Event事件之间的关系。 我需要基于这些引用,构建一个清晰、逐步的解释。回答必须包括: - 所有行内数学表达式使用$...$格式(但这个问题不涉及数学,所以可能不需要)。 - 独立公式使用$$...$$(同样不适用)。 - LaTeX语法正确(不适用)。 - 生成相关问题。 - 在回答中引用的段落末尾添加引用标识,如[^1]。 回答结构: 1. **介绍**:简要解释delegate和event在C#中的基本概念。 2. **核心区别**:基于引用[2]和[^3],列出关键差异。 3. **Unity中的实践**:使用引用[1]中的例子来说明在Unity中的应用。 4. **总结**:强调主要区别和最佳实践。 5. **相关问题**:生成2-3个相关问题。 关键点从引用: - 引用[2]:委托无封装,事件有封装;事件隐藏触发权,外部只能订阅(+=/-=)。 - 引用[3]:委托是框架需要方法体,事件是方法体需要参数(需要澄清)。 - 引用[1]:UnityEvent是Unity的特定实现,支持序列化和Inspector绑定。 组织回答: 1. **定义和基本概念**: - Delegate:类似于函数指针,用于回调。 - Event:基于委托的封装,提供更安全的订阅机制。 2. **主要区别**: - 封装性:事件封装了委托,防止外部直接调用或赋值。 - 安全性:事件避免了空引用风险,委托可能被外部置空。 - 用途:委托用于基础回调;事件用于模块间通信,如UI交互。 - 在Unity中:UnityEvent是Unity的特定事件系统,支持序列化。 3. **Unity中的例子**: - 使用引用[1]的代码片段:UnityEvent.AddListener和.Invoke。 4. **总结表**: - 基于引用[2]的对比表,简化呈现。 生成相关问题: - 例如:1. 如何在Unity中使用自定义事件?2. UnityEvent和标准C#事件有什么区别?3. 事件在观察者模式中的应用? 确保引用: - 在回答中提到引用时,添加[^1], [^2], [^3]。 最终回答结构: 1. 引言 2. 区别解释 3. Unity实践 4. 总结 5. 相关问题 </think> ### Unity中Event和Delegate的区别 在Unity开发中,委托(Delegate)和事件(Event)都是用于实现回调机制的核心概念,但它们的设计目的、安全性和应用场景有显著差异。下面我将基于C#语言特性和Unity实践,逐步解释两者的区别,帮助你更清晰地理解它们的使用方式。 #### 1. **基本概念** - **委托(Delegate)**:本质上是一个类型安全的函数指针,用于定义方法签名(参数和返回类型)。它可以存储对多个方法的引用,并通过`+=`或`-=`操作符动态添加或移除方法。委托的核心作用是实现回调机制,例如在异步操作完成后触发特定方法。 - 示例:定义一个委托类型 `public delegate void MyDelegate(int num);`,然后可以赋值或调用它。 - **风险**:委托是公开的,外部代码可以直接调用、覆盖或置空它,可能导致空引用异常(NullReferenceException)[^2]。 - **事件(Event)**:是委托的封装版本,基于委托实现但增加了访问控制。事件只允许外部通过`+=`(订阅)和`-=`(取消订阅)操作符来添加或移除处理方法,而触发权(即调用委托)被限制在定义事件的类内部。这提供了更好的封装性和安全性。 - 示例:在C#中定义事件 `public event MyDelegate OnEvent;`,只能在本类内部调用 `OnEvent?.Invoke(10);`。 - **优势**:事件天然支持观察者模式,避免外部代码破坏订阅链,降低崩溃风险[^2]。在Unity中,常用`UnityEvent`(一个内置事件系统)来实现Inspector序列化和可视化绑定[^1]。 #### 2. **核心区别** 下表总结了委托和事件的关键差异(基于引用资料): | 特性 | 委托 (Delegate) | 事件 (Event) | |------|-----------------|--------------| | **封装性** | 无封装,委托实例完全公开,外部可读写。 | 封装了委托,隐藏触发权(Invoke),外部仅能订阅(`+=`/`-=`)。 | | **外部可操作性** | 外部代码可直接调用、赋值或覆盖委托(例如 `myDelegate = null;`)。 | 外部只能添加或移除处理方法,无法直接调用或修改事件本身。 | | **空值风险** | 高:外部可能置空委托,导致调用时崩溃(NullReferenceException)。 | 低:内部控制触发,自动处理空引用(例如使用 `?.Invoke()`)。 | | **典型用途** | 基础回调、函数参数(如排序算法中的比较委托)。 | 模块间通信、观察者模式(如UI按钮点击、游戏状态变化)。 | | **Unity应用** | 用于底层逻辑(如自定义回调)。 | 常用`UnityEvent`实现Inspector序列化,方便拖拽绑定方法[^1][^2]。 | 关键点解释: - **封装性差异**:事件本质上是委托的“包装器”,它限制外部访问,确保只有事件发布者能触发它。这防止了外部代码意外调用或重置委托链[^2]。 - **执行机制**: - 委托需要“方法体”:当委托被调用时,它执行所有已订阅的方法(例如 `myDelegate.Invoke(5);` 会触发所有关联方法)。 - 事件需要“参数”:事件本身不存储方法逻辑,只定义参数签名;订阅者提供方法体,事件触发时传入参数完成调用[^3]。 - **Unity特定实现**:Unity提供了`UnityEvent`类,它是事件的一种实现,支持泛型(如 `UnityEvent<int>`)和Inspector可视化。例如: ```csharp public UnityEvent myEvent; // 无参数事件 void Start() { myEvent.AddListener(noArg); // 添加订阅 myEvent.Invoke(); // 仅在类内部触发 } void noArg() { Debug.Log("No Arg"); } ``` 这里,`AddListener` 替代了原生的 `+=`,更适合Unity的序列化需求[^1]。 #### 3. **在Unity中的实践差异** - **委托的典型问题**:如果委托被外部置空,可能导致运行时崩溃。例如: ```csharp public MyDelegate myDelegate; // 外部代码可能执行 myDelegate = null; 然后调用时崩溃。 ``` - **事件的安全性**:事件内部处理空引用,避免崩溃。在Unity中,推荐使用`UnityEvent`因为它: - 支持序列化:事件可在Inspector中配置,拖拽方法绑定(如按钮点击事件)。 - 促进解耦:例如,UI组件订阅游戏逻辑事件,无需直接引用对方[^1][^2]。 - **性能考量**:事件和委托在性能上相似,但事件的封装增加了少量开销(可忽略)。在Unity中,`UnityEvent`优化了多播调用。 #### 4. **总结** - **使用委托**:当需要灵活的回调机制,且风险可控时(如在内部类中使用)。 - **使用事件**:当需要安全的事件发布-订阅模型时,尤其在跨脚本通信中(如玩家受伤事件触发UI更新)。Unity的`UnityEvent`是首选,因为它简化了工作流并避免空引用错误[^2][^3]。 在实践中,事件更适合Unity游戏开发,因为它增强了代码健壮性和模块化。委托则适合底层工具方法。如果你在项目中使用事件,总是优先考虑`UnityEvent`以利用Unity的集成功能。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

abeniaj0427

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值