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)
To solve the "Argument of type 'unknown' is not assignable to parameter of type" TypeScript error, use a type assertion or a type guard to verify that the argument is of the expected type. The error is caused when a value of type unknown is passed to a function.
You could probably get around it by passing an array. You could probably get around it by passing an array. Click to expand... No, since you can't pass any parameters to Invoke.
The Action type specifies a function that returns no value through a return statement. The second Action receives two int parameters. The third Action receives no parameters.
The unknown type is the type-safe counterpart of any. If you are certain that the specific value has a compatible type, but TypeScript doesn't know about it, use a type assertion , e.g. value as RightType when calling the function. Type assertions are used when we have information about the type of a value that TypeScript can't know about.
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.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With