c# - 如何动态调用具有已知参数基类型的委托(delegate)?

标签 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> 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.
}

一切正常。我现在想做的是实现一些“魔法”以允许使用实际派生类型调用消息处理程序,如下所示:

private bool MessageHandler(MyMessage message) {
    // Handle the message.
}

这意味着跨越数千个脚本的消息处理代码将不需要费心将 Message 对象转换为正确的派生类型——它已经是该类型了。这会方便得多。我觉得这可以通过使用泛型、委托(delegate)、表达式树、协变和/或逆变来以某种方式实现,但我就是不明白!

我一直在尝试很多不同的事情,感觉我越来越接近了,但我就是做不到。这些是我能够提出的两个部分解决方案:

// 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.
    }
}

部分解决方案 1 工作正常,但它使用了反射,考虑到性能,这并不是一个真正的选择 - 这是一个游戏,消息系统将被大量使用。部分解决方案 2 有效,但前提是 T 泛型参数可用。消息传递系统还将有一个消息对象队列,它将处理这些消息对象,并且 T 在那里不可用。

有什么办法可以实现吗?如果有人能提供任何帮助,我将不胜感激!

最后要注意的是,我使用的是 Unity,它使用 Mono - .NET 4 不是一个选项,据我所知,这排除了使用“动态”的可能性。

最佳答案

如果我正确理解了您的问题,您希望有一些通用的方法来存储和调用消息处理程序,同时为每个处理程序提供特定的消息类型。

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

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 作为一个值保存,并在您的 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);
    }
}

这样你就可以在没有通用参数的情况下调用 SendMessage 并在你的实际处理程序方法中获取类型化参数。

关于c# - 如何动态调用具有已知参数基类型的委托(delegate)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18113201/

相关文章:

c# - 在单声道中使用 c# 驱动程序比较 mongo 集合的两个字段

c# - 如何以存储库模式读取 iCollection 数据 - ASP.NET- MVC Entity Framework

c# - 如何将 T 转换为类以匹配 "where T : class"约束?

android studio : linux, windows,性能不佳

android - iOS/Android 的 Mono 打开还是关闭?付费还是免费?

C# 在垃圾收集之前查找特定类的未使用对象

c# - 获取第一个表插入的值到第二个表

C# 到 Mono GetConsoleWindow 异常

java - 当您可以只使用具体类型时,为什么会有带有上限的通配符?

java - 修复此铸件行为的最简洁方法