Search code examples
c#.netlambdadelegates

Invoke async Func stored in a Dictionary


I have a class that is responsible of storing a bunch of async Funcs to execute them later. The signature of each Func differs only on the Task type. Here is the class:

public class FuncStore<TParameters> {
  private readonly Dictionary<string, object> _funcCache = new();
  
  public void AddFunc<T>(string key, Func<TParameters, Task<T>> value) {
    _funcCache.Add(key, value);
  }
  
  public async Task ExecuteAllAsync(TParameters parameters) {       
    foreach(var (_, func) in _funcCache) {
      
      // Problem here. How do I invoke each lambda?          

    }
  }
}

Here is the parameters class for context:

public class DefaultParameters {
  public string SomeProperty { get; set; }
}

And here is an example of use:

var store = new FuncStore<DefaultParameters>();
var lambda01 = (DefaultParameters parameters) => {
  var str = parameters.SomeProperty + "01"; // Whatever
  return Task<string>.FromResult(str); // Simulate async op
};
var lambda02 = (DefaultParameters parameters) => {
  var number = Convert.ToInt32(parameters.SomeProperty) + 2; // Whatever
  return Task<int>.FromResult(number); // Simulate async op
};

// These both lambdas differ on their return type
// one is Task<string> and the other is Task<int>
store.AddFunc("01", lambda01);
store.AddFunc("02", lambda02);

// Many other lambdas and additions later...
var parameters = new DefaultParameters();
await store.ExecuteAllAsync(parameters);

The problem is that I don't know how to invoke those lambdas. Here are my attempts so far:

  public async Task ExecuteAllAsync(TParameters parameters) {       
    foreach(var (_, func) in _funcCache) {
      
      // Problem here. How do I invoke each lambda?
      
      // Attempt 1:
      var typedFunc = func as Func<TParameters, Task<object>>; // NullReferenceExeption, the cast fails
      await typedFunc(parameters);
      
      // Attempt 2 (almost):
      var asDelegate = func as Delegate;
      // I manage to get the task...
      var task = (Task)asDelegate.DynamicInvoke(new object[]{ parameters });
      // ... but if I await it, I get a compile error: "Argument 1: cannot convert from 'void' to 'bool'"
      Console.WriteLine(await task);
    }
    
    await Task<int>.FromResult(0); // Just to make the compiler happy while I debug
  }

Here is the .NET Fiddle.

Any ideas? Thanks in advance.


Solution

  • One way is to use dynamic, in both the function cache and the delegates:

    var lambda01 = (DefaultParameters parameters) => {
        var str = parameters.SomeProperty + "01"; // Whatever
        return Task.FromResult<dynamic>(str); // Simulate async op
    };
    

    and:

    private readonly Dictionary<string, dynamic> _funcCache = new();
    
    public void AddFunc(string key, Func<TParameters, Task<dynamic>> value) {
        _funcCache.Add(key, value);
    }
    

    Working example: https://dotnetfiddle.net/rTm8ZM