Cra*_* W. 25 c# compiler-construction roslyn visual-studio-2013 visual-studio-2015
我昨天在Visual Studio 2015中打开了我们的解决方案,我们的一些单元测试(在Visual Studio 2013中运行正常)开始失败.Digger更深入我发现这是因为召集GetTypes()一个程序集返回了不同的结果.我已经能够创建一个非常简单的测试用例来说明它.
在Visual Studio 2013和2015中,我使用.NET Framework 4.5.2创建了一个新的控制台应用程序.我在以下两个项目中都添加了以下代码.
class Program
{
static void Main(string[] args)
{
var types = typeof(Program).Assembly.GetTypes()
.Where(t => !t.IsAbstract && t.IsClass);
foreach (var type in types)
{
Console.WriteLine(type.FullName);
}
Console.ReadKey();
}
}
Run Code Online (Sandbox Code Playgroud)
当我在Visual Studio 2013中运行时,我得到以下输出(如预期的那样).
VS2013Example.Program
当我在Visual Studio 2015中运行时,我得到以下输出(不是预期的).
VS2015Example.Program
VS2015Example.Program + <>Ç
那是什么VS2015Example.Program+<>c类型的?原来它是.Where()方法中的lambda .是的,这是正确的,不知何故,本地lambda作为一种类型暴露.如果我.Where()在VS2015中注释掉,那么我就不再获得第二行了.
我已经使用Beyond Compare来比较两个.csproj文件,但唯一的区别是VS版本号,项目GUID,默认命名空间和程序集的名称,VS2015有一个对System.Net.Http的引用, VS2013一个没有.
有没有人见过这个?
有没有人解释为什么局部变量将在程序集级别作为类型公开?
Yuv*_*kov 29
有没有人见过这个?
是的,这是由用于提升lambda表达式的新编译器行为引起的.
以前,如果lambda表达式没有捕获任何局部变量,它将被缓存为调用站点的静态方法,这使得编译器团队需要跳过一些箍以正确对齐方法参数和this参数.Roslyn中的新行为是所有lambda表达式都被提升到一个显示类中,其中委托作为显示类中的实例方法公开,忽略它是否捕获任何局部变量.
如果您在Roslyn中反编译您的方法,您会看到:
private static void Main(string[] args)
{
IEnumerable<Type> arg_33_0 = typeof(Program).Assembly.GetTypes();
Func<Type, bool> arg_33_1;
if (arg_33_1 = Program.<>c.<>9__0_0 == null)
{
arg_33_1 = Program.<>c.<>9__0_0 =
new Func<Type, bool>(Program.<>c.<>9.<Main>b__0_0);
}
using (IEnumerator<Type> enumerator = arg_33_0.Where(arg_33_1).GetEnumerator())
{
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current.FullName);
}
}
Console.ReadKey();
}
[CompilerGenerated]
[Serializable]
private sealed class <>c
{
public static readonly Program.<>c <>9;
public static Func<Type, bool> <>9__0_0;
static <>c()
{
// Note: this type is marked as 'beforefieldinit'.
Program.<>c.<>9 = new Program.<>c();
}
internal bool <Main>b__0_0(Type t)
{
return !t.IsAbstract && t.IsClass;
}
}
Run Code Online (Sandbox Code Playgroud)
旧编译器在哪里,你会看到:
[CompilerGenerated]
private static Func<Type, bool> CS$<>9__CachedAnonymousMethodDelegate1;
private static void Main(string[] args)
{
IEnumerable<Type> arg_34_0 = typeof(Program).Assembly.GetTypes();
if (Program.CS$<>9__CachedAnonymousMethodDelegate1 == null)
{
Program.CS$<>9__CachedAnonymousMethodDelegate1 =
new Func<Type, bool>(Program.<Main>b__0);
}
IEnumerable<Type> types =
arg_34_0.Where(Program.CS$<>9__CachedAnonymousMethodDelegate1);
foreach (Type type in types)
{
Console.WriteLine(type.FullName);
}
Console.ReadKey();
}
[CompilerGenerated]
private static bool <Main>b__0(Type t)
{
return !t.IsAbstract && t.IsClass;
}
Run Code Online (Sandbox Code Playgroud)
您可以通过过滤掉CompilerGenerated附加了属性的类来获得所需的结果:
var types = typeof(Program)
.Assembly
.GetTypes()
.Where(t => !t.IsAbstract &&
t.IsClass &&
Attribute.GetCustomAttribute(
t, typeof (CompilerGeneratedAttribute)) == null);
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅我的问题在Roslyn中委派缓存行为更改