Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

invoke Action with unknown type parameter (Action<any>)

Tags:

c#

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)
like image 289
Henrik Karlsson Avatar asked May 31 '13 10:05

Henrik Karlsson


People also ask

How to solve the “argument of type “unknown” is not assignable to parameter of type?

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.

Can you pass a parameter to an invoke statement?

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.

What is an action type in Python?

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.

What is an unknown type in typescript?

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.


2 Answers

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.

like image 134
Chris Sinclair Avatar answered Sep 22 '22 09:09

Chris Sinclair


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
}
like image 34
Jon Avatar answered Sep 25 '22 09:09

Jon