使用未知类型参数(Action <any>)</any>调用Action

时间:2013-05-31 10:16:11

标签: c#

我正在创建一个API,我的目标是公开一个可以像这样调用的方法:

Library.AddCallback<string>(Type.ChatMessage, GotMessage);

private void GotMessage(string message) {
    //...
}
//or
Library.AddCallback<int>(Type.Number, GotNumber);

private void GotNumber(int number) {
    //...
}

<int><string>类型可以是任何类型。

在库中,方法如下所示:

public void AddCallback<T1>(object type, Action<T1> callback) {
    //...
}

问题是我想以某种方式首先在方法调用之外(在列表中)保存回调,然后才能调用它。

我理想的做法是首先将其投射到一个对象,以便能够将其存储在List<object>中,然后再将其强制转换为Action<T1>。然而,似乎不可能将T1保存到变量中(除非通过执行typeof(T1)拒绝我使用它来将其转换回来)。

我想如何调用它的示例(我从列表中获得typedata的位置):

((Action<type>)callback)(data)

2 个答案:

答案 0 :(得分:5)

我不确定如何输入data。假设它现在是object类型,您可以将Action<T>分解为Action<object>并在其中执行转换:

private List<Action<object>> Callbacks = new List<Action<object>>();

public void AddCallback<T1>(object type, Action<T1> callback) 
{
    Callbacks.Add((data) => callback((T1)data));
}

public void FireCallback(object data)
{
    Action<object> callback = GetCallback();
    callback(data);
}

编辑:你已经将它标记为答案,但这是另一个将回调存储在类型集中的实现。

CallbackHandler存储键入的回调列表:

public class CallbackHandler<T> : ICallbackHandler
{
    private List<Action<T>> Callbacks = new List<Action<T>>();

    public void AddCallback<T>(Action<T> callback)
    {
        Callbacks.Add(callback);
    }

    public void Callback(object data)
    {
        T typedData = (T)data;
        foreach(var callback in Callbacks)
            callback(typedData);
    }
}

public interface ICallbackHandler
{
    void Callback(object data);
}

然后您的更高级别Library有这样的内容:

private Dictionary<Type, ICallbackHandler> AllCallbacks = new Dictionary<Type, ICallbackHandler>();

public void AddCallback<T>(Action<T> callback) 
{
    Type type = typeof(T);
    ICallbackHandler handler;
    if (!AllCallbacks.TryGetValue(type, out handler))
    {
        handler = new CallbackHandler<T>();
        AllCallbacks[type] = handler;
    }
    CallbackHandler<T> typedHandler = (CallbackHandler<T>)handler;
    typedHandler.AddCallback(callback);
}

public void FireCallback(object data)
{
    Type type = data.GetType();
    ICallbackHandler handler;
    AllCallbacks.TryGetValue(type, out handler);
    if (handler != null)
        handler.Callback(data);
}

这假设data的类型决定了要触发的回调。如果您需要再添加一个级别(基于Type.ChatMessageType.Number),则不应该太难。

答案 1 :(得分:1)

您可以这样做:

List<object> callbacks = new List<object>();

public void AddCallback<T1>(object type, Action<T1> callback)
{
    this.callbacks.Add(callback);
}

public IEnumerable<Action<T>> GetCallbacks<T>()
{
    return this.callbacks.OfType<Action<T>>();
}

并像这样使用它:

// Sample callbacks
static void Foo(int i) { Console.WriteLine("Foo {0}", i); }
static void Bar(string s) { Console.WriteLine("Bar {0}", s); }

AddCallback(null, (Action<int>)(Foo));
AddCallback(null, (Action<string>)(Bar));

foreach (var callback in GetCallbacks<int>())
{
    callback(42); // only calls Foo
}