2

考虑这个简单的例子:

class Foo
{
    public int a;
    public int b;
    public int c;
    public List<Foo> foos; // This complicates matters a bit
}

现在我想计算任何成员的总和/最小值/最大值/平均值等 - 包括Foo孩子。我想为此创建一个通用功能,因此我不必重复代码。

我想像这样的函数调用:

double sum = Calculate<double>(someFoo, sum => (f => f.a));
int count = Calculate<int>(someFoo, count => (f => 1 + foo.Length));

因此,对 的任意成员的任意操作Foo。这可以在 C# 4.0 中完成吗?例如使用动作

4

3 回答 3

2

你可以这样做。你的语法有点不对劲。它看起来像这样:

double sum = Calculate<double>(someFoo, f => f.a);
int count = Calculate<int>(someFoo, f => 1 + f.Length);

该方法如下所示:

public T Calculate<T>(Foo foo, Func<Foo, T> calculator)
{
    return calculator(foo);
}

但是,所有这些对于Foo. 更有可能的是,someFoo应该真的是someFoos,即多个对象。此外,我猜您也希望能够指定聚合方法。
那会变成Calculate这样:

public T Calculate<T>(IEnumerable<Foo> foo, Func<Foo, T> calculator,
                      Func<IEnumerable<T>, T> aggregate)
{
    return aggregate(foo.Select(calculator));
}

用法:

List<Foo> someFoos = ...;
var sum = Calculate(someFoos, x => x.a, Enumerable.Sum)
var count = Calculate(someFoos, x => 1 + x.Length, Enumerable.Count)

为了使其全部递归,最简单的方法是使用一个方法将Foo对象及其所有子对象作为平面列表返回:

public IEnumerable<Foo> Flatten(Foo foo)
{
    yield return foo;
    foreach(var child in foo.Children.SelectMany(Flatten))
        yield return child;
}

使用此方法Calculate会导致:

public T Calculate<T>(IEnumerable<Foo> foo, Func<Foo, T> calculator,
                      Func<IEnumerable<T>, T> aggregate)
{
    return aggregate(Flatten(foo).Select(calculator));
}

说了这么多,写了这么多,我要问:为什么不直接使用普通的LINQ呢?

Flatten(foo).Select(f => f.a).Sum();
Flatten(foo).Select(f => 1 + f.Length).Count();
于 2013-02-26T09:58:30.950 回答
2

编写一个帮助函数,为您提供所有Foos:

IEnumerable<Foo> SelfAndDescendants
{
  get
  {
    yield return this;
    foreach(var child in foos)
      foreach(var descendant in SelfAndDescendants(child)
        yield return descendant;
  }
}

然后您可以简单地使用普通 LINQ 进行聚合:root.SelfAndDescendents.Sum(f=>f.a)


如果您想进一步推动可重用性,您可以使用通用辅助函数:

public static IEnumerable<T> DepthFirstTopDownTraversal(T root, Func<T, IEnumerable<T>> children)
{
    Stack<T> s=new Stack<T>();
    s.Push(root);
    while(s.Count>0)
    {
      T current = s.Pop();
      yield return current;
      foreach(var child in children(current))
        s.Push(child);
    }
}

然后实现SelfAndDescendatsreturn DepthFirstTopDownTraversal(this, f=>f.foos);.

于 2013-02-26T10:07:24.673 回答
0

是的,你可以这么做。根据你要调用Calculate方法的方式,可以定义如下:

private T Calculate<T>(Foo foo, Func<Foo, Func<Foo, T>> func) {
    //do something here...
}

编辑:也许它没有太大意义,所以如果你能给我们提供一个预期输入/输出的例子,那将会很有用。

于 2013-02-26T09:59:49.757 回答