Behavior of Assembly.GetTypes() changed in Visual Studio 2015

在升级到Visual Studio 2015后,发现调用Assembly.GetTypes()返回的结果包含了编译器生成的类型,如匿名方法或Lambda表达式的载体类。这是由于Roslyn编译器改变了对Lambda表达式的处理方式。
摘要由CSDN通过智能技术生成

Behavior of Assembly.GetTypes() changed in Visual Studio 2015

 

I opened our solution in Visual Studio 2015 yesterday and a few of our unit tests (which ran fine in Visual Studio 2013) starting failing. Digger deeper I discovered it was because calling GetTypes() on an assembly was returning different results. I've been able to create a very simple test case to illustrate it.

In both Visual Studio 2013 and 2015 I created a new console application using .NET Framework 4.5.2. I put the following code in both projects.

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(); } }

When I run in Visual Studio 2013 I get the following output (as expected).

VS2013Example.Program

When I run in Visual Studio 2015 I get the following output (not as expected).

VS2015Example.Program

VS2015Example.Program+<>c

So what is that VS2015Example.Program+<>c type? Turns out it's the lambda inside the .Where()method. Yes, that's right, somehow that local lambda is being exposed as a type. If I comment out the .Where() in VS2015 then I no longer get that second line.

I've used Beyond Compare to compare the two .csproj files but the only differences are the VS version number, the project GUID, names of the default namespace and assembly, and the VS2015 one had a reference to System.Net.Http that the VS2013 one didn't.

Has anyone else seen this?

Does anyone have an explanation as to why a local variable would be being exposed as a type at the assembly level?

 

Has anyone else seen this?

Yes, this is caused by the new compiler behavior for lifting lambda expressions.

Previously, if a lambda expression didn't capture any local variables, it would be cached as a static method at the call site, which made the compiler team need to jump some hoops in order to properly align the method arguments and the this parameter. The new behavior in Roslyn is that all lambda expressions get lifted into a display class, where the delegate is exposed as an instance method in the display class, disregarding if it captures any local variables.

If you decompile your method in Roslyn, you see this:

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; } }

Where's with the old compiler, you'd see this:

[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; }

You can get the desired result by filtering out classes that have the CompilerGenerated attribute attached to them:

var types = typeof(Program) .Assembly .GetTypes() .Where(t => !t.IsAbstract && t.IsClass && Attribute.GetCustomAttribute( t, typeof (CompilerGeneratedAttribute)) == null);

For more, see my question Delegate caching behavior changes in Roslyn

转载于:https://www.cnblogs.com/sgnah/p/5657216.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值