是否可以组合不同类型的代理(使用返回值作为参数)?

时间:2016-06-23 12:26:10

标签: c# generics delegates

我有一个集合,我首先需要过滤,然后从中选择一个,但是如何处理集合取决于一些参数。所以我和两位代表一起去了,但不知怎的,我应该把它们结合起来:

delegate IEnumerable<T> FilterDelegate(IEnumerable<T> collection);
delegate T SelectorDelegate(IEnumerable<T> collection, ref T previous);

//Combine above two to this one:
delegate T GetItemDelegate(IEnumerable<T> collection, ref T previous);

我尝试了类似的东西,但它失败了,因为委托不是匹配的类型:

static GetItemDelegate CreateDelegate(FilterDelegate filter, SelectorDelegate select)
{
    return Delegate.Combine(filter, select) as GetItemDelegate;
}

问题:

是否可以创建一个新的委托来调用第一个委托并使用返回值作为第二个的输入参数?如果没有ref参数,我可以使用lambdas,但当然我不能使用outref

可编辑的例子:

namespace DelegateTest
{
   interface INumericValue
   {
       int Number { get; }
   }
   class Test : INumericValue
   {
       public Test(int i)
       {
           Number = i;
       }
       public int Number { get; }
   }
   class Program
   {
      delegate IEnumerable<T> FilterDelegate<T>(IEnumerable<T> collection) where T : class, INumericValue;
      delegate T SelectDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;

      delegate T CombinedDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;

       static void Main()
       {
           Test previous = new Test(6);
           List<Test> collection = new List<Test>();
           FilterDelegate<Test> filter = Filter;
           SelectDelegate<Test> select = Select;

           CombinedDelegate<Test> combined = Delegate.Combine(filter, select) as CombinedDelegate<Test>;

           for (int i = 0; i < 10; i++)
               collection.Add(new Test(i));

           //Expected result Test with Number = 7
           Test result = combined(collection, ref previous);
       }

       static IEnumerable<T> Filter<T>(IEnumerable<T> collection) where T : class, INumericValue
       {
          return collection.Where(c => c.Number > 3);
       }
       static T Select<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue
       {
           var previousNumber = previous.Number;
           return previous = collection.FirstOrDefault(c => c.Number > previousNumber);
       }
   }
}

3 个答案:

答案 0 :(得分:2)

你可以这样做,但不确定它比链接调用要干净得多:

class Program {
    delegate IEnumerable<T> FilterDelegate<T>(IEnumerable<T> collection) where T : class, INumericValue;

    delegate T SelectDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;

    delegate T CombinedDelegate<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue;

    static void Main() {
        Test previous = new Test(6);
        List<Test> collection = new List<Test>();
        FilterDelegate<Test> filter = Filter;
        SelectDelegate<Test> select = Select;
        for (int i = 0; i < 10; i++)
            collection.Add(new Test(i));
        // use explicit types to be able to use ref in lambda
        CombinedDelegate<Test> combined = (IEnumerable<Test> c, ref Test p) => @select(filter(c), ref p);
        Test result = combined(collection, ref previous);
        //Expected result Test with Number = 7
    }

    static IEnumerable<T> Filter<T>(IEnumerable<T> collection) where T : class, INumericValue {
        return collection.Where(c => c.Number > 3);
    }

    static T Select<T>(IEnumerable<T> collection, ref T previous) where T : class, INumericValue {
        // this didn't compile so I changed it.
        foreach (var item in collection) {
            if (item.Number > previous.Number) {
                previous = item;
                break;
            }
        }
        return previous;
    }
}

答案 1 :(得分:2)

您可以使用匿名方法使用refout参数:

static GetItemDelegate<T> CreateDelegate<T>(FilterDelegate<T> filter, SelectorDelegate<T> select)
{
    return delegate (IEnumerable<T> collection, ref T previous) {
        return select(filter(collection), ref previous);
    };
}

答案 2 :(得分:2)

这几乎是对Lee的答案的评论,因为这只是他对旧式C#2.0&#34;匿名方法&#34;的答案。转换为C#3.0 lambda语法:

static GetItemDelegate<T> CreateDelegate<T>(FilterDelegate<T> filter, SelectorDelegate<T> select)
{
  return
   (IEnumerable<T> collection, ref T previous) => select(filter(collection), ref previous);
}

我们注意到C#3.0于2007年发布(Visual Studio 2008)。

备注:您的委托类型的正确声明是:

delegate IEnumerable<T> FilterDelegate<T>(IEnumerable<T> collection);
delegate T SelectorDelegate<T>(IEnumerable<T> collection, ref T previous);

//Combine above two to this one:
delegate T GetItemDelegate<T>(IEnumerable<T> collection, ref T previous);

第一个当然与Func<IEnumerable<T>, IEnumerable<T>>完全相似。