考虑这个简单的例子:
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中完成吗?例如。使用Actions?
答案 0 :(得分: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();
答案 1 :(得分:2)
编写一个帮助函数,为您提供所有Foo
s:
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);
}
}
然后将SelfAndDescendats
实施为return DepthFirstTopDownTraversal(this, f=>f.foos);
。
答案 2 :(得分:0)
是的,你可以这样做。根据您想要调用Calculate
方法的方式,您可以按如下方式定义它:
private T Calculate<T>(Foo foo, Func<Foo, Func<Foo, T>> func) {
//do something here...
}
编辑:也许它没有多大意义,所以如果您能为我们提供预期输入/输出的示例,那将会非常有用。