C# 如何使用已知的参数基类型动态调用委托?

C# 如何使用已知的参数基类型动态调用委托?,c#,generics,mono,unity3d,messaging,C#,Generics,Mono,Unity3d,Messaging,我正在尝试为Unity游戏实现我自己的消息传递系统。我有一个基本的工作版本-一个非常简单的例子如下: // Messaging class: private Dictionary<Type, List<Func<Message, bool>>> listeners; // Set up by some other code. public void AddListener<T>(Func<Message, bool> listene

我正在尝试为Unity游戏实现我自己的消息传递系统。我有一个基本的工作版本-一个非常简单的例子如下:

// Messaging class:

private Dictionary<Type, List<Func<Message, bool>>> listeners; // Set up by some other code.

public void AddListener<T>(Func<Message, bool> listener) where T : Message {
    this.listeners[typeof(T)].Add(listener);
}

public void SendMessage<T>(T message) where T : Message {
    foreach (Func<Message, bool> listener in this.listeners[typeof(T)]) {
        listener(message);
    }
}

// Some other class:

private void Start() {
    messaging.AddListener<MyMessage>(this.MessageHandler); // Subscribe to messages of a certain type.
}

private bool MessageHandler(Message message) { // We receive the message as the Message base type...
    MyMessage message2 = (MyMessage)message; // ...so we have to cast it to MyMessage.
    // Handle the message.
}
这意味着跨越数千个脚本的消息处理代码将不需要麻烦地将消息对象强制转换为正确的派生类型——它已经是该类型了。那会方便得多。我觉得这可以通过泛型、委托、表达式树、协方差和/或逆变实现,但我就是不明白

我已经尝试了很多不同的事情,感觉自己越来越接近了,但我就是无法做到。以下是我能想到的两个局部解决方案:

// Messaging class:

private Dictionary<Type, List<Delegate>> listeners; // Delegate instead of Func<Message, bool>.

public void AddListener<T>(Func<T, bool> listener) where T : Message { // Func<T, bool> instead of Func<Message, bool>.
    this.listeners[typeof(T)].Add(listener);
}

public void SendMessage<T>(T message) where T : Message {
    foreach (Delegate listener in this.listeners[typeof(T)]) {
        listener.Method.Invoke(method.Target, new object[] { message }); // Partial solution 1.
        ((Func<T, bool>)listener)(message); // Partial solution 2.
    }
}
//消息传递类:
专用字典侦听器;//委托而不是Func。
public void AddListener(Func listener),其中T:Message{//Func代替Func。
this.listeners[typeof(T)].Add(listener);
}
公共无效发送消息(T消息),其中T:message{
foreach(此.listeners[typeof(T)]中的委托侦听器){
listener.Method.Invoke(Method.Target,新对象[]{message});//部分解决方案1。
((Func)listener)(message);//部分解决方案2。
}
}
部分解决方案1工作正常,但它使用反射,考虑到性能,反射实际上不是一个选项-这是一个游戏,消息传递系统将被大量使用。部分解决方案2起作用,但仅当T通用参数可用时有效。消息传递系统还将有一个它将处理的消息对象队列,而T在那里不可用

有没有办法做到这一点?我将非常感谢任何人能提供的任何帮助


最后要注意的一点是,我使用的是Unity,它使用的是Mono-.NET 4,就我所知,这排除了使用“动态”的可能性。如果我正确理解了您的问题,您希望有一些存储和调用消息处理程序的通用方法,同时为每个处理程序提供特定的消息类型

一种方法是使用基本类型的接口处理程序和泛型类型的实现:

interface IMessageHandler
{
    bool ProcessMessage(Message m);
}

class MessageHandler<T>: IMessageHandler where T:Message
{
    Func<T, bool> handlerDelegate;

    public MessageHandler(Func<T, bool> handlerDelegate)
    {
        this.handlerDelegate = handlerDelegate; 
    }

    public bool ProcessMessage(Message m)
    {
        handlerDelegate((T)m);  
    }
}
接口IMessageHandler
{
bool ProcessMessage(消息m);
}
类MessageHandler:IMessageHandler,其中T:Message
{
Func handlerDelegate;
公共消息处理程序(Func handlerDelegate)
{
this.handlerDelegate=handlerDelegate;
}
公共bool ProcessMessage(消息m)
{
handlerDelegate((T)m);
}
}
处理程序字典应将IMessageHandler作为值保存,并在AddListener方法中创建MessageHandler并将其添加到处理程序字典中。像这样:

private Dictionary<Type, IMessageHandler> listeners;

public void AddListener<T>(Func<T, bool> listener) where T : Message 
{ 
    this.listeners[typeof(T)].Add(new MessageHandler<T>(listener));
}

public void SendMessage(Message message) 
{
    foreach (IMessageHandler listener in this.listeners[message.GetType()]) 
    {
       listener.ProcessMessage(message);
    }
}
私有字典侦听器;
public void AddListener(Func listener),其中T:Message
{ 
this.listeners[typeof(T)].Add(newmessagehandler(listener));
}
公共无效发送消息(消息消息)
{
foreach(此.listeners[message.GetType()]中的IMessageHandler侦听器)
{
ProcessMessage(message);
}
}

这样,您就可以在不使用泛型参数的情况下调用SendMessage,并在实际的处理程序方法中获取类型化参数。

基本上,您会发现Func无法指向bool方法(派生的d)。因此,没有安全的类型来存储侦听器的句柄

我认为一种方法可能是放弃侦听器的通用字典,使容器类成为通用类。因此,每种类型都有消息泵(或至少容器)。像这样的

abstract class BaseMessagePump
{
    protected abstract void SendMessage(Message m);

    public void AddListener<T>(Func<T, bool> l) where T : Message
    {
        //check a dictionary / cache
        new GenericMessageQueue<T>().listeners.Add(l);
    }
}

class GenericMessageQueue<T> : BaseMessagePump where T : Message
{
    public List<Func<T, bool>> listeners;

    protected override void SendMessage(Message m) 
    {
        listeners[0]((T)m);//the cast here is safe if you do correct cache / type checking in the base add listener
    }
}
抽象类BaseMessagePump
{
受保护的抽象无效发送消息(消息m);
public void AddListener(Func l),其中T:Message
{
//检查字典/缓存
新建GenericMessageQueue().listeners.Add(l);
}
}
类GenericMessageQueue:BaseMessagePump,其中T:Message
{
公开名单听众;
受保护的覆盖无效发送消息(消息m)
{
侦听器[0]((T)m);//如果在基本添加侦听器中执行正确的缓存/类型检查,则此处的强制转换是安全的
}
}

Wow,这是一个非常简单的解决方案,效果非常好!非常感谢。:)
abstract class BaseMessagePump
{
    protected abstract void SendMessage(Message m);

    public void AddListener<T>(Func<T, bool> l) where T : Message
    {
        //check a dictionary / cache
        new GenericMessageQueue<T>().listeners.Add(l);
    }
}

class GenericMessageQueue<T> : BaseMessagePump where T : Message
{
    public List<Func<T, bool>> listeners;

    protected override void SendMessage(Message m) 
    {
        listeners[0]((T)m);//the cast here is safe if you do correct cache / type checking in the base add listener
    }
}