私の理解では、あなたの質問は間違った前提に基づいているようです。推論を再構築できるかどうか見てみましょう。
- リンク先の記事では、自動生成されたシーケンスが「怠lazな」動作をどのように示すかを説明し、これが直感に反する結果をもたらす方法を示しています。
- したがって、IEnumerableの特定のインスタンスがこの遅延動作を示すかどうかは、自動的に生成されるかどうかを確認することで検出できます。
- それ、どうやったら出来るの?
問題は、2番目の前提が間違っていることです。与えられたIEnumerableがイテレータブロック変換の結果であるかどうかを検出できたとしても(そして、そうする方法はあります)、仮定が間違っているので助けにはなりません。理由を説明しましょう。
class M { public int P { get; set; } }
class C
{
public static IEnumerable<M> S1()
{
for (int i = 0; i < 3; ++i)
yield return new M { P = i };
}
private static M[] ems = new M[]
{ new M { P = 0 }, new M { P = 1 }, new M { P = 2 } };
public static IEnumerable<M> S2()
{
for (int i = 0; i < 3; ++i)
yield return ems[i];
}
public static IEnumerable<M> S3()
{
return new M[]
{ new M { P = 0 }, new M { P = 1 }, new M { P = 2 } };
}
private class X : IEnumerable<M>
{
public IEnumerator<X> GetEnumerator()
{
return new XEnum();
}
// Omitted: non generic version
private class XEnum : IEnumerator<X>
{
int i = 0;
M current;
public bool MoveNext()
{
current = new M() { P = i; }
i += 1;
return true;
}
public M Current { get { return current; } }
// Omitted: other stuff.
}
}
public static IEnumerable<M> S4()
{
return new X();
}
public static void Add100(IEnumerable<M> items)
{
foreach(M item in items) item.P += 100;
}
}
よし、4つの方法があります。S1およびS2は自動的に生成されるシーケンスです。S3およびS4は手動で生成されたシーケンスです。今、私たちが持っていると仮定します:
var items = C.Sn(); // S1, S2, S3, S4
S.Add100(items);
Console.WriteLine(items.First().P);
S1とS4の結果は0になります。シーケンスを列挙するたびに、作成されたMへの新しい参照を取得します。S2とS3の結果は100になります。シーケンスを列挙するたびに、前回取得したMへの同じ参照を取得します。シーケンスコードが自動的に生成されるかどうかは、列挙されたオブジェクトに参照IDがあるかどうかの問題とは直交します。自動生成と参照IDの2つのプロパティは、実際には互いに関係ありません。あなたがリンクした記事はそれらを幾分圧縮します。
参照IDを持つオブジェクトを常に提供するものとしてシーケンスプロバイダーが文書化されていない限り、そうすることを想定するのは賢明ではありません。
ICollection<T>
すべてのコレクションがであるとは限らないため、具体的ではなくインターフェースを使用し、さらに下にキャストすることをお勧めしますList<T>
。たとえば、配列はPoint[]
実装されますIList<T>
が、そうではありませんList<T>
。