Invoke Action with an unknown type parameter (Action <any>)

I am creating an API, and my goal is to expose a method that can be called as follows:

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 somehow I want to first save the callback outside the method call (in the list), and then later it can be called.

What I ideally want to do is first pass it to the object so that it can save it to a List<object> , and then return it back to Action<T1> . However, it seems that it is impossible to save T1 in a variable (except for what typeof(T1) does, which prevents me from using it to return it).

An example of what I would like to call it (where I got type and data from the list):

 ((Action<type>)callback)(data) 
+3
c #
source share
2 answers

I'm not sure how data is typed. Assuming this is an object type at the moment, you can decompose the Action<T> into an Action<object> and cast to 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 already marked it as an answer, but here is another implementation that stores callbacks in a typed set.

A CallbackHandler stores the entered callback list:

 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); } 

The data type is supposed to determine which callbacks should fire. If you need to overlay another layer (based on Type.ChatMessage or Type.Number ), this should not be too complicated.

+4
source share

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 as follows:

 // 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 } 
+1
source share

All Articles