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

すべて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);
    }
}

として実装SelfAndDescendatsreturn DepthFirstTopDownTraversal(this, f=>f.foos);ます。

于 2013-02-26T10:07:24.673 に答える
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);
}

ただし、これらすべてが の 1 つのインスタンスでは意味を成しません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 に答える
0

はい、できます。メソッドを呼び出すCalculate方法に応じて、次のように定義できます。

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

編集:おそらくあまり意味がないので、予想される入力/出力の例を提供していただけると助かります。

于 2013-02-26T09:59:49.757 に答える