是什么原因导致这个列表在被单向调用时通过引用传递,但是按值传递另一个?

Tra*_*s J 11 c#

我正在为运行验证方法做一个简单的测试,并遇到了这种奇怪的情况.

public IEnumerable<int> ints (List<int> l)
{
 if(false)yield return 6;
 l.Add(4);
}


void Main()
{
 var a = new List<int>();
 var b = new List<int>();
 for( int i = 0; i < 4; i++ ){
  a.Add(i);
  b.Add(i);
 }
 a.AddRange(ints(a));
 ints(b);
 Console.WriteLine(a);
 Console.WriteLine(b);
}
Run Code Online (Sandbox Code Playgroud)

一旦此代码运行,a将包含[0,1,2,3,4].但是,b将包含[0,1,2,3].为什么调用方法作为参数AddRange允许列表通过引用传递?或者,如果没有发生,那做了什么?

Ale*_*kov 15

ints(b)call不会枚举IEnumerable,所以代码永远不会到达l.Add(4)行,这与AddRange枚举所有项目以将它们添加到列表的情况不同.

b手动查看案例枚举结果,请执行以下操作:

ints(b).ToList();
Run Code Online (Sandbox Code Playgroud)

IEnumerable<T>通过函数实现的函数不会在枚举开始之前执行函数体 - 代码实际上是由编译器转换为具有状态的类,以支持可枚举的真正延迟评估(详细信息可以在多篇文章中找到,即Iterator模式揭秘 - 链接由Tim提供Schmelter).

  • @Tigran [这是一个简单的程序](http://ideone.com/rbZM6g),它演示了只有在枚举枚举器时才运行代码.它与***无法访问的代码无关. (4认同)
  • @Tigran`ints`返回一个具有良好定义行为的IEnumerable,如果你不**枚举可枚举的预期行为是**枚举永远不会执行**.延迟执行的行为是LINQ建立在Linq2Sql之类的基础上的完整基础. (3认同)
  • 这不是一个错误,它完全是设计的.如果您不了解它可能会出乎意料,但对于一个bug来说,对于那些设计和编写此编译器的人来说,它必须是出乎意料的,事实并非如此. (2认同)