visual studio references community c# visual-studio-2013 compiler-construction visual-studio-2015 roslyn

c# - community - references visual studio 2013



Comportamiento de Assembly.GetTypes() cambiado en Visual Studio 2015 (1)

¿Alguien más ha visto esto?

Sí, esto es causado por el nuevo comportamiento del compilador para levantar expresiones lambda.

Anteriormente, si una expresión lambda no capturaba ninguna variable local, se almacenaba en caché como un método estático en el sitio de la llamada, lo que hacía que el equipo del compilador necesitara saltar algunos aros para alinear correctamente los argumentos del método y el parámetro. El nuevo comportamiento en Roslyn es que todas las expresiones lambda se elevan a una clase de visualización, donde el delegado se expone como un método de instancia en la clase de visualización, sin tener en cuenta si captura cualquier variable local.

Si descompilas tu método en Roslyn, ves esto:

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

¿Dónde está el viejo compilador? Verás esto:

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

Puede obtener el resultado deseado filtrando las clases que tienen asociado el atributo CompilerGenerated :

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

Para más información, vea mi pregunta Cambios en el comportamiento del caché del delegado en Roslyn

Abrí nuestra solución en Visual Studio 2015 ayer y algunas de nuestras pruebas unitarias (que funcionaron bien en Visual Studio 2013) comenzaron a fallar. Digger más profundo Descubrí que era porque llamar a GetTypes() en un ensamblado devolvía resultados diferentes. Pude crear un caso de prueba muy simple para ilustrarlo.

Tanto en Visual Studio 2013 como en 2015, creé una nueva aplicación de consola usando .NET Framework 4.5.2. Puse el siguiente código en ambos proyectos.

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

Cuando corro en Visual Studio 2013 obtengo el siguiente resultado (como se esperaba).

VS2013Ejemplo.Programa

Cuando corro en Visual Studio 2015 obtengo el siguiente resultado (no como se esperaba).

VS2015Ejemplo.Programa

VS2015Ejemplo.Programa + <> c

Entonces, ¿qué es ese VS2015Example.Program+<>c type? Resulta que es el lambda dentro del método .Where() . Sí, así es, de alguna manera esa lambda local está siendo expuesta como un tipo. Si hago un comentario de .Where() en VS2015, ya no obtendré esa segunda línea.

He usado Beyond Compare para comparar los dos archivos .csproj, pero las únicas diferencias son el número de versión VS, el GUID del proyecto, los nombres del ensamblado y el espacio de nombres predeterminado, y el VS2015 tenía una referencia a System.Net.Http que indicaba que VS2013 uno no.

¿Alguien más ha visto esto?

¿Alguien tiene una explicación de por qué una variable local se estaría exponiendo como un tipo en el nivel de ensamblado?