0

There is some code that I'm trying to convert from IList to IEnumerable:

[Something(123)]
public IEnumerable<Foo> GetAllFoos()
{
  SetupSomething();

  DataReader dr = RunSomething();
  while (dr.Read())
  {
    yield return Factory.Create(dr);
  }
}

The problem is, SetupSomething() comes from the base class and uses:

Attribute.GetCustomAttribute(
    new StackTrace().GetFrame(1).GetMethod(), typeof(Something))

yield ends up creating MoveNext(), MoveNext() calls SetupSomething(), and MoveNext() does not have the [Something(123)] attribute.

I can't change the base class, so it appears I am forced to stay with IList or implement IEnumerable manually (and add the attribute to MoveNext()).

Is there any other way to make yield work in this scenario?

4

5 に答える 5

3

スタック フレーム機能が必要な場合は、反復子 (yield) を使用できません。お気づきのとおり、これにより、メソッドが を実装するカスタム クラスに書き換えられますIEnumerable<T>

ただし、これを次のように簡単に作り直すことができます。

[Something(123)]
public IEnumerable<Foo> GetAllFoos()
{
  SetupSomething();

  List<Foo> results = new List<Foo>();
  DataReader dr = RunSomething();
  while (dr.Read())
  {
    results.Add(Factory.Create(dr));
  }
  return results;
}

イテレータの遅延実行は失われますが、適切に機能します。

于 2010-04-07T17:32:18.670 に答える
1

あなたの説明から、問題はSetupSomethingがスタックトレース上の直接の呼び出し元だけを見ているということのように聞こえます。もう少し上を見ると(呼び出し元の呼び出し元)、GetAllFocus呼び出しと目的の属性が見つかります。

頭の中で思い出せませんが、クラスがまだMoveNext()実装を実装していないという理由だけで、yieldがMoveNext()実装を作成している場合は、独自のMoveNextを実装し、それに属性を設定すると、yieldはMoveNext()を見つけて使用しますか?ただの大げさな推測。

于 2010-04-07T17:35:30.463 に答える
1

必要なすべての前処理を行う別のメソッドでメソッドをラップできます。

[Something(123)]
public IEnumerable<Foo> GetAllFoos()
{
    SetupSomething();
    return GetAllFoosInternal();
}

private IEnumerable<Foo> GetAllFoosInternal()
{
    DataReader dr = RunSomething();
    while (dr.Read())
    {
        yield return Factory.Create(dr);
    }
}
于 2010-04-07T17:32:37.660 に答える
1

このように、メソッドを分割していただけますか?

[Something(123)]
public void GetAllFoosHelper()
{
  SetupSomething(); 
}

public IEnumerable<Foo> GetAllFoos() 
{ 
  GetAllFoosHelper();

  DataReader dr = RunSomething(); 
  while (dr.Read()) 
  { 
    yield return Factory.Create(dr); 
  } 
} 
于 2010-04-07T17:33:41.940 に答える
1

おそらく何かが足りないのですが、ここで属性を使用する意味がわかりません。次のように書いてもよいでしょう。

public IEnumerable<Foo> GetAllFoos()
{
  SetupSomething(123);
  // etc..
}

全体のヘコファロットも高速です。さらに安全なことに、JIT コンパイラーが SetupSomething() をインライン化すると、あなたは水に沈んでしまいます。

于 2010-04-07T18:11:08.910 に答える