2013-02-26 28 views
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中完成嗎?例如。使用Actions

回答

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

你可以這樣做。雖然你的語法有點偏離。它應該是這樣的:

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(); 
+0

我還不夠清楚。 「計算」功能會是什麼樣子? (我相信需要兩個動作。) – l33t 2013-02-26 09:59:43

+0

@ l33t:請參閱更新。我想這就是你的意思。 – 2013-02-26 10:03:47

0

是的,你可以做到這一點。根據你要撥打的Calculate法的方式,你可以如下定義它:

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

編輯:也許它沒有太多的感覺,所以這將是有用的,如果你能爲我們提供一個預期的輸入/輸出的例子。