通过Func <t,tresult =“”>其中TResult未知</t,>

时间:2010-11-15 21:46:49

标签: c# generics lambda

注意:请重新标记和/或重新命名


我有一个类FooEnumerator,它包装Foo并实现IEnumerable<FooEnumerator>Foo表示树状数据结构,枚举的FooEnumerator是当前节点的子节点。

Foo是供应商提供的数据对象。 FooEnumerator实现了一堆自定义过滤代码。

class FooEnumerator : IEnumerable<FooEnumerator>
{
    public Foo WrappedNode { get; private set; }
    public string Name { get { return WrappedNode.Name; } }
    public int Id { get{ return WrappedNode.Id; } }
    public DateTime Created { get{ return WrappedNode.Created; } }

    public FooEnumerator(Foo wrappedNode)
    {
        WrappedNode = wrappedNode;
    }

    public IEnumerator<FooEnumerator> GetEnumerator()
    {
          foreach (Foo child in this.GetChildren())
              if(FilteringLogicInHere(child))
                    yield return new FooEnumerator(child);
    }

    ...
}

我希望能够使用在创建顶级FooEnumerator时定义的给定(任意)表达式对树的每个级别进行排序,并将此表达式传递给每个要使用的新枚举项。

我想使用lambda来定义sort表达式,就像使用OrderBy函数一样。事实上,我打算将lambda传递给OrderBy

OrderBy的签名是

OrderBy<TSource, TKey>(Func<TSource, TKey> keySelector)

其中TKey是给定Func的返回类型,但在方法签名中是一个类型参数,在编译时计算出来。

使用示例

var x = GetStartingNode();

var sort = n => n.DateTime;
var enu = new FooEnumerator(x, sort);

var sort2 = n => n.Name;
var enu2 = new FooEnumerator(x, sort2);

然后,排序表达式将存储在类变量中,FooEnumerator的工作方式如下:

// pseudo-implementation

private Expression<Func<Foo, TKey>> _sortBy;

public FooEnumerator(Foo wrappedNode, Expression<Func<Foo, TKey>> sortBy)
{
    WrappedNode = wrappedNode;
    _sortBy = sortBy;
}

public IEnumerator<FooEnumerator> GetEnumerator()
{
    foreach (Foo child in this.GetChildren().OrderBy(_sortBy))
        if(FilteringLogicInHere(child))
            yield return new FooEnumerator(child);
}

如何在此用例中指定TKey的类型(隐式或显式)?

我不想硬编码,因为我希望能够对基础Foo的任何和所有属性进行排序。

2 个答案:

答案 0 :(得分:4)

好吧,您无法创建Expression<Func<Foo,TKey>>类型的成员委托变量,因为从未指定TKey。但是,您可以创建Expression<Func<Foo,IComparable>>类型的成员,这可能足以满足您的目的。当然,您可能需要更改FooEnumerator构造函数以接受此签名。

编辑:其他人建议您对FooEnumerator进行参数设置,使其接受TKey。你当然可以做到这一点,但你应该意识到出现的问题:

  1. 通过参数化枚举器,您就可以开始行动。任何想要存储FooEnumerator<T>的代码都必须具有{{1}类型的先验知识。 }。但是,您可以实现非通用接口T来处理它。
  2. 如果您希望将来支持对多个字段进行排序,则参数化枚举器会产生问题。 C#不支持具有可变数量类型参数的泛型,这限制了创建需要的泛型多个任意类型。这个问题很难处理,因为开始创建IFooEnumeratorFooEnumerator<T>FooEnumerator<T1,T2>等等很尴尬。

答案 1 :(得分:1)

您还可以参数化您的枚举器:

class FooEnumerator<TKey> {

  // ... All your 'pseudo' code would work here

}

我建议使用IComparable对接口进行编程。