【问题标题】:Storing and calling generically-typed delegates存储和调用泛型委托
【发布时间】:2012-02-01 18:23:57
【问题描述】:

如果不诉诸反思,我正在寻找的东西可能是不可能的。如果是这样,我仍然想知道实现它的最佳方法。

基本上,这就是我希望我的代码看起来的样子:

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

我的问题是,如何将这些代表(每个都有不同的签名)存储在 MyClass 的列表中?以及如何在以后需要时调用它们,将每个参数的返回值用作下一个参数的输入参数?

MyClass 的内部很可能是一堆 List 之类的东西。但我什至不确定从哪里开始。

(本来想打电话给new MyClass&lt;int, string, Warehouse, IList&lt;Supplier&gt;&gt;(),但是因为没有“类型参数数组”,所以放弃了。)

【问题讨论】:

  • 为什么不instance.Add&lt;int, IList&lt;Supplier&gt;&gt;(x =&gt; Warehouse.LookupByName(x.ToString()).Suppliers);,甚至var supplierLookupFunction = new Func&lt;Int, IEnumerable&lt;Supplier&gt;&gt;(x =&gt; Warehouse.LookupByName(x.ToString()).Suppliers);
  • 请不要在标题前加上“C# Generics:”之类的前缀。标签做得更好。

标签: c# generics delegates


【解决方案1】:

好吧,您可以将它们全部存储为Delegate - 但棘手的是稍后调用它们。

如果您能够随时验证下一个委托的类型是否正确,例如通过为“当前输出”保留Type 引用,您始终可以存储List&lt;Func&lt;object, object&gt;&gt; 并使您的Add 方法类似于:

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

然后调用它们:

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

【讨论】:

  • 这就是我要回答的问题。这里讨论了一个类似的解决方案:stackoverflow.com/questions/3813261/…,在这种情况下使用 Action 而不是 Func,因为它们不需要返回值。
  • 我有这个解决方案的修改版本几乎可以工作,所以我认为根据我的问题,这个答案几乎是完美的。现在,它不提供链有效的编译时强制......我也可能会考虑 Servy 的 Linq-esque 答案。我可能愿意每次都输入instance = instance.Chain&lt;T&gt;(blah);,如果这意味着我会在编译时检查序列是否有效。
  • @ElliotNelson:是的,你当然可以这样做——你甚至可以拥有一个对象,然后只返回某种“令牌”(带有返回列表的内部引用)来保存泛型。
【解决方案2】:

Linq Select 语句本质上就是这样做的……

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

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

您还可以将每个中间 Select 调用存储为 Enumerable,以便在 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】:

      如果你想进行编译时类型检查,你所做的听起来很像普通的泛型委托。假设存储 Added 的各个函数(除了 Int 到 String 的转换)并在以后组合它们有一些价值,您可以执行以下操作:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多