Storing and calling generically-typed delegates

爷,独闯天下 提交于 2021-02-07 12:14:43

问题


What I'm looking for is probably not going to be possible without resorting to reflection. If that's the case, I'd still want to know the best way to pull it off.

Essentially, this is what I want my code to look like:

var instance = new MyClass();
instance.Add<int, string>(x => x.ToString());
instance.Add<string, Warehouse>(x => Warehouse.LookupByName(x));
instance.Add<Warehouse, IList<Supplier>>(x => x.Suppliers());

instance.Chain(3);    // should call each lambda expression in turn

My question is, how can I store these delegates, each with a different signature, in a list in MyClass? And how can I call them later on when I want to, using the return value from each one as the input parameter to the next one?

The inside of MyClass may very well be a mess of List's and all that. But I'm not even sure where to start on this.

(Originally, I wanted to call new MyClass<int, string, Warehouse, IList<Supplier>>(). However, since there's no "type parameter array", I gave up on that approach.)


回答1:


Well, you could store them all as Delegate - but the tricky thing is invoking them later.

If you're able to validate that the next delegate at any time is of the right type, e.g. by holding a Type reference for "the current output" you could always store a List<Func<object, object>> and make your Add method something like:

public void Add<TIn, TOut>(Func<TIn, TOut> func)
{
    // TODO: Consider using IsAssignableFrom etc
    if (currentOutputType != typeof(TIn))
    {
        throw new InvalidOperationException(...);
    }
    list.Add(o => (object) func((TIn) o));
    currentOutputType = typeof(TOut);
}

Then to invoke them all:

object current = ...; // Wherever
foreach (var func in list)
{
    current = func(current);
}



回答2:


The Linq Select statement essentially does this...

var temp = instance.Select(x => x.ToString())
.Select(x => WareHouse.LookupByName(x))
.Select(x=> x.Suppliers());

List<List<Suppliers>> = temp.ToList(); //Evaluate statements

You can also store each intermediate Select call as an Enumerable to have the stated method you use in the OP.




回答3:


class Program
{
    static void Main(string[] args)
    {
        var instance = new MyClass();
        instance.Add<int, string>(i => i.ToString());
        instance.Add<string, int>(str => str.Length);
        instance.Add<int, int>(i => i*i);

        Console.WriteLine(instance.Chain(349));
        Console.ReadLine();

    }
}

public class MyClass
{
    private IList<Delegate> _Delegates = new List<Delegate>();

    public void Add<InputType, OutputType>(Func<InputType, OutputType> action)
    {
        _Delegates.Add(action);
    }

    public object Chain<InputType>(InputType startingArgument)
    {
        object currentInputArgument = startingArgument;
        for (var i = 0; i < _Delegates.Count(); ++i)
        {
            var action = _Delegates[i];
            currentInputArgument = action.DynamicInvoke(currentInputArgument);
        }
        return currentInputArgument;
    }
}



回答4:


If you want compile time type checking, what you are doing sounds suspiciously like plain old generic delegates. Assuming that there is some value to storing the individual functions that were Added (other than the Int to String conversion) and composing them later, you can do something like this:

var lookupWarehouseByNumber = new Func<int, Warehouse>(i => Warehouse.LookupByName(i.ToString()));
var getWarehouseSuppliers = new Func<Warehouse, IEnumerable<Supplier>>(w => w.Suppliers);
var getWarehouseSuppliersByNumber = new Func<int, IEnumerable<Supplier>>(i => getWarehouseSuppliers(lookupWarehouseByNumber(i)));


来源:https://stackoverflow.com/questions/9101139/storing-and-calling-generically-typed-delegates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!