c# - Assembly.GetTypes() 的行为在 Visual Studio 2015 中发生了变化

标签 c# visual-studio-2013 compiler-construction visual-studio-2015 roslyn

我昨天在 Visual Studio 2015 中打开了我们的解决方案,我们的一些单元测试(在 Visual Studio 2013 中运行良好)开始失败。深入挖掘我发现这是因为调用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();
    }
}

当我在 Visual Studio 2013 中运行时,我得到以下输出(如预期的那样)。

VS2013Example.Program

当我在 Visual Studio 2015 中运行时,我得到以下输出(与预期不同)。

VS2015Example.Program

VS2015Example.Program+<>c

那是什么VS2015Example.Program+<>c类型?结果是.Where()里面的lambda方法。是的,没错,本地 lambda 以某种方式作为一种类型公开。如果我注释掉 .Where()在 VS2015 中,我不再得到第二行。

我使用 Beyond Compare 比较了这两个 .csproj 文件,但唯一的区别是 VS 版本号、项目 GUID、默认命名空间和程序集的名称,而 VS2015 文件引用了 System.Net。 VS2013 没有的 Http。

有没有人看到这个?

有没有人解释为什么局部变量会在程序集级别公开为类型?

最佳答案

Has anyone else seen this?

是的,这是由提升 lambda 表达式的新编译器行为引起的。

以前,如果 lambda 表达式没有捕获任何局部变量,它将作为静态方法缓存在调用站点,这使得编译器团队需要跳一些圈子才能正确对齐方法参数和这个 参数。 Roslyn 中的新行为是所有 lambda 表达式都被提升到显示类中,其中委托(delegate)作为显示类中的实例方法公开,而不管它是否捕获任何局部变量。

如果您在 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;
    }
}

旧编译器在哪里,你会看到这个:

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

您可以通过过滤掉附加了 CompilerGenerated 属性的类来获得所需的结果:

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

有关更多信息,请参阅我的问题 Delegate caching behavior changes in Roslyn

关于c# - Assembly.GetTypes() 的行为在 Visual Studio 2015 中发生了变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31542076/

相关文章:

c - MSVC 和优化常量表达式

c# - 如果文本框为空,则禁用按钮

c# - 使用 Bouncy CaSTLe 将 RSA 公钥转换为 RFC 4716 公钥

c# - 当行的字节位置已知时,从 C# 中的文件中删除一行文本

c++ - 使用非原子 bool 时的未定义行为

c++ - Visual Studio 2013 C++类属性初始化问题

c# - WPF和模式MVVM如何从文本框中获取值,将其发送到ViewModel并保存dto模型

visual-studio-2013 - ExcludeFilesFromDeployment 在 Visual Studio 2013 Publish Web 中不起作用

c++ - Visual Studio 还是 GCC?

c++ - Visual Studio 2012 : Compiler setup (possible to use Compiler from SDK eg. VC++2008-编译器 VC++2010-编译器)