关于Visual Studio 2010中使用的C#/.NET编译器的问题:在项目开发过程中,一位同事遇到了使用锁内现有代码时VS2010编译器会崩溃的情况。我们逐行分解代码,最终得出结论:通过 lock 语句中的数组在
yield return
中使用 foreach
会使编译器崩溃。可以使用以下代码重现该问题:
using System;
using System.Collections.Generic;
namespace PlayGround
{
public static class Program
{
private static readonly object SyncRoot = new object();
private static IEnumerable<int> EnumerableWithLock()
{
lock (SyncRoot)
{
foreach (var i in new int[1])
{
yield return i;
}
}
}
public static void Main()
{
foreach (var i in EnumerableWithLock())
{
Console.WriteLine(i);
}
}
}
}
我们继续在 Visual Studio 2013 上测试此复制示例,并且没有出现相同的问题。这个编译器问题似乎与 VS2010 中使用的编译器有关,并且在 VS2012 中可能有也可能没有相同的问题(我们无法出于测试目的访问它)。此外,我们已经测试过使用常规
for
循环不会崩溃。因此问题是,为什么 VS2010 编译器会崩溃?它做了什么让它如此困惑?
(是的,这主要是为了兴趣而了解编译器的问题)
好吧,也许这不能解决问题,但这是我的研究..
理论上,Jon Skeet 表示,yield 与锁一起使用时根本不会产生任何问题,因为分别在第一个和最后一个“MoveNext”迭代块之前和之后获取和释放锁。
更多信息这里。
当我亲自尝试你的代码时,编译器抛出以下(内部)错误:
Error 6 Internal Compiler Error (0xc0000005 at address 1332695D): likely culprit is 'TRANSFORM'.
An internal error has occurred in the compiler. To work around this problem, try simplifying or changing the program near the locations listed below. Locations at the top of the list are closer to the point at which the internal error occurred. Errors such as this can be reported to Microsoft by using the /errorreport option.
ConsoleApp1
但是,对类进行以下修改是有效的:
public static class Program
{
//private static readonly object SyncRoot = new object();
//private static IEnumerable<int> EnumerableWithLock()
//{
// lock (SyncRoot)
// {
// foreach (var i in new int[1])
// {
// yield return i;
// }
// }
//}
public static void Main()
{
SomeClass sc = new SomeClass();
foreach (var i in sc.EnumerableWithLock())
{
Console.WriteLine(i);
}
Console.ReadLine();
}
}
public class SomeClass
{
private static readonly object SyncRoot = new object();
int[] i = { 1, 2, 3, 4, 5 };
List<int> retval = new List<int>();
public IEnumerable<int> EnumerableWithLock()
{
lock (SyncRoot)
{
foreach (var number in i)
{
retval.Add(number);
}
return retval;
}
}
}
所以可能只是一个 CSC bug 或者更微妙的东西。