对委托相关知识的总结,参考了一些博客再加上了自己的理解。

delegate、Func、Action、event、UnityEvent、UnityAction

委托

一种可用于封装命名或者匿名方法的引用类型。

它的存在说明变量不仅能存储值,对象的引用,还能存方法。只不过声明时前面加个delegate。与函数指针不是同个概念。

类似于 C++ 中的函数指针,而且是类型安全和可靠的。

可用于简化代码,实现观察者模式。

一、自定义委托类型:

delegate 返回值 方法名(参数)

二、.NET自带泛型委托类型:

省去自定义委托类型

1、Action< T >:只能委托无返回值的方法

2、Func< T , Result >只能委托有返回值的方法,可有参数数量:0~5

// 一、显式委托声明 -- 定义一个签名:
delegate double MathFunc(double num);

class DelegateTest
{
    // 符合委托声明的常规方法
    double Double(double input)
    {
        return input * 2;
    }

	void Output(double result)
	{       
		Console.WriteLine(result);
	}

    void Main()
    {
        // 1、使用一个命名方法实例化委托类型
        MathFunc mf1 = Double;
        // 调用委托实例
        double res = mf1(233);

        // 2、用匿名方法来实例化委托类型
        mf1 = delegate(double input)
        {
            return input * input;
        };
        res = mf1(233);

        // 3、用Lambda表达式来实例化委托类型
        mf1 = s => s * s * s;
        res = mf1(4);
        
        //二、1、实例化Func<T, TResult>、Action<T>委托类型(省去显式自定义委托)
        Func<double, double> mf2 = Double;
        Action<double> mf3 = Output;
        
        // 2、用匿名方法
        mf2 = delegate(double input)
        {
            return input * input;
        };
        // 3、用Lambda表达式
        mf2 = s => s * s * s;
        
        res = mf2(233);
    }
}

三、多重委托:event

相当于一个方法的容器,调用event时,便调用容器中的所有方法。因此可用于实现观察者模式。

可用+=、-=增删相应的委托类型的方法。直接以方法的形式调用,或者以Invoke()调用。

注意,在方法内部声明event会报错。

示例代码:

class EventTest
{   
    //1、声明包含Func<double, double>类型的委托的event
    event Func<double, double> mfe; 
    void Main()
    {
        Func<double, double> ma1 = s => s * s;
        Func<double, double> ma2 = s => s * s * s; 
        
        //2、增加包含的方法
        mfe += ma1;
        mfe += ma2;
        
        //3、调用所有方法
        double res = mfe(233);  
    }
}

使用event实现发布-订阅模式:

Idol.cs:发布者

using UnityEngine;

public class Idol : MonoBehaviour {
    public delegate void IdolBehaviour(string behaviour);
    //设置为静态以让粉丝们能订阅、退订(增删委托方法)
    public static event IdolBehaviour IdolDoSomethingHandler;

    private void Start()
    {
        //Idol 决定搞事了, 如果他还有粉丝的话, 就必须全部都通知到
        if (IdolDoSomethingHandler != null)
        {
        	//所有的粉丝都接收到消息(参数),并行动(执行)
            IdolDoSomethingHandler("Idol give up writing.");
        }
    }
}

SubscriberA.cs:订阅者A

using UnityEngine;

public class SubscriberA : MonoBehaviour {
    /// <summary>
    /// OnEnable在该脚本被启用时调用,你可以把它看做路转粉的开端
    /// </summary>
    private void OnEnable()
    {
        //粉丝通过订阅偶像来获取偶像的咨询, 并在得到讯息后执行相应的动作
        Idol.IdolDoSomethingHandler += LikeIdol;
    }

    /// <summary>
    /// OnEnable在该脚本被禁用时调用,你可以把它看做粉转路的开端
    /// </summary>
    private void OnDisable()
    {
        Idol.IdolDoSomethingHandler -= LikeIdol;
    }

    /// <summary>
    /// 粉丝A是一个脑残粉
    /// </summary>
    /// <param name="idolAction"></param>
    public void LikeIdol(string idolAction)
    {
        print(idolAction + " I will support you forever!");
    }
}

SubscriberB.cs:订阅者B

using UnityEngine;

public class SubscriberB : MonoBehaviour {
    /// <summary>
    /// OnEnable在该脚本被启用时调用,你可以把它看做路转粉的开端
    /// </summary>
    private void OnEnable()
    {
        //粉丝通过订阅偶像来获取偶像的咨询, 并在得到讯息后执行相应的动作
        Idol.IdolDoSomethingHandler += HateIdol;
    }

    /// <summary>
    /// OnEnable在该脚本被禁用时调用,你可以把它看做粉转路的开端
    /// </summary>
    private void OnDisable()
    {
        Idol.IdolDoSomethingHandler -= HateIdol;
    }

    /// <summary>
    /// 粉丝B是一个无脑黑
    /// </summary>
    /// <param name="idolAction"></param>
    public void HateIdol(string idolAction)
    {
        print(idolAction + " I will hate you forever!");
    }
}

四、Unity的再包装:UnityAction、UnityEvent

Unity独有的这两个类分别对应Action,event。与Event不同的是,UnityEvent序列化后,可以在Editor面板上显示,增删包含的方法(UnityAction)。

Unity所使用的监听器(例如OnClick)都是UnityEvent类。

作为一个对象,而不是一个有特殊待遇的event。它要按对象的方式来使用。可继承。

AddListener():注册UnityAction

RemoveListener:取消注册UnityAction

Invoke():调用所有UnityAction

示例代码:

using UnityEngine;
using UnityEngine.Events;

//使用Serializable序列化IdolEvent, 否则无法在Editor中显示
[System.Serializable]
public class IdolEvent : UnityEvent<string> {
	//......
}

public class Idol : MonoBehaviour {
    public IdolEvent idolEvent;

    private void Start()
    {
        if (idolEvent == null)
        {
            idolEvent = new IdolEvent();
        }
        idolEvent.Invoke("Idol give up writing.");
    }
}

使用后的Editor面板:

unity eventtrigger没反应 unity delegate event_自定义


参考

https://docs.unity3d.com/ScriptReference/Events.UnityEvent.html