据我了解,您的问题似乎基于不正确的前提。让我看看我是否可以重构推理:
- 链接到的文章介绍了自动生成的序列如何表现出“惰性”行为,并说明了这如何导致违反直觉的结果。
- 因此,我可以通过检查是否自动生成来确定给定的IEnumerable实例是否将表现出这种惰性行为。
- 我怎么做?
问题在于第二个前提是错误的。即使您可以检测到给定的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;
}
}
好吧,我们有四种方法。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相同的引用。序列代码是否自动生成与所枚举的对象是否具有参照身份的问题正交。这两个属性-自动生成和引用身份-实际上彼此无关。您链接到的文章对其进行了一些补充。
除非序列提供者被记录为总是提供具有引用身份的对象,否则假设这样做是不明智的。
ICollection<T>
这将是更好的选择,因为并非所有集合都是这样List<T>
。例如,数组Point[]
实现IList<T>
但不是List<T>
。