Reputation: 5723
I am creating an API, and my goal is to expose a method that can be invoked like this:
Library.AddCallback<string>(Type.ChatMessage, GotMessage);
private void GotMessage(string message) {
//...
}
//or
Library.AddCallback<int>(Type.Number, GotNumber);
private void GotNumber(int number) {
//...
}
The type <int>
, <string>
can be any type.
In the library, the method looks something like this:
public void AddCallback<T1>(object type, Action<T1> callback) {
//...
}
The problem is that I somehow want to first save the callback outside of the method call (in a list) and then later be able to call it.
What I ideally want to do is to first cast it to an object to be able to store it in a List<object>
and then cast it back to Action<T1>
. However it seems like it is impossible to save T1 to a variable (except by doing typeof(T1)
which denies me to use it to cast it back).
Example of how I would like to call it (where I got type
and data
from a list):
((Action<type>)callback)(data)
Upvotes: 5
Views: 2087
Reputation: 437414
You can do something like this:
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>>();
}
And use it like this:
// 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
}
Upvotes: 2
Reputation: 23208
I'm not sure how data
is typed. Assuming it's type object
for now, you can decompose the Action<T>
to Action<object>
and perform a cast in them:
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);
}
EDIT: You've already marked it as an answer, but here's another implementation that stores the callbacks in a typed set.
A CallbackHandler
stores the typed list of callbacks:
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);
}
Then your higher level Library
has something like this:
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);
}
This is assuming that the type of data
determines which callback(s) to fire. If you need to put one more level on it (based on Type.ChatMessage
or Type.Number
) it shouldn't be too difficult.
Upvotes: 6