为什么泛型类中重复嵌套类型的字段声明会导致源代码大幅增加?

2024-03-24

场景非常罕见,但非常简单:定义一个泛型类,然后创建一个继承自外部类的嵌套类,并在嵌套内定义一个关联字段(自类型)。代码片段比描述更简单:

class Outer<T>
{
    class Inner : Outer<Inner>
    {
        Inner field;
    }
}

IL反编译后,C#代码如下所示:

internal class Outer<T>
{
    private class Inner : Outer<Outer<T>.Inner>
    {
        private Outer<Outer<T>.Inner>.Inner field;
    }
}

这似乎很公平,但是当您更改字段的类型声明时,事情就会变得更加棘手。所以当我将字段声明更改为

Inner.Inner field;

反编译后该字段将如下所示:

private Outer<Outer<Outer<T>.Inner>.Inner>.Inner field;

我明白,类的“嵌套”和继承不太兼容,但是为什么我们会观察到这种行为?是个 Inner.Inner 类型声明完全改变了类型吗?是 Inner.Inner and Inner 在这种情况下,类型在某种程度上有所不同?

当事情变得非常棘手时

您可以看到反编译源代码 http://codepaste.net/6f63no对于下面的课程。它确实很大,总长度有 12159 个符号。

class X<A, B, C>
{
    class Y : X<Y, Y, Y>
    {
        Y.Y.Y.Y.Y.Y y;
    }
} 

最后,这个类:

class X<A, B, C, D, E>
{
    class Y : X<Y, Y, Y, Y, Y>
    {
        Y.Y.Y.Y.Y.Y.Y.Y.Y y;
    }
}

结果是27.9 MB (29,302,272 bytes)组装和Total build time: 00:43.619

使用的工具

编译是在 C# 5 和 C# 4 编译器下完成的。反编译由dotPeek完成。构建配置:Release and Debug


你问题的核心是为什么Inner.Inner是一个不同的类型Inner。一旦理解了这一点,您对编译时间和生成的 IL 代码大小的观察就很容易理解。

首先要注意的是,当你有这个声明时

public class X<T>
{
  public class Y { }
}

与名称相关的类型有无限多种Y。每个泛型类型参数都有一个T, so X<int>.Y不同于X<object>.Y,并且,对以后很重要,X<X<T>>.Y是一个不同的类型X<T>.Y对全部T的。您可以针对各种类型进行测试T.

接下来要注意的是,在

public class A
{
  public class B : A { }
}

有无数种方法可以引用嵌套类型B。一是A.B,另一个是A.B.B, 等等。该声明typeof(A.B) == typeof(A.B.B)回报true.

当你按照你所做的方式将这两者结合起来时,就会发生一些有趣的事情。方式Outer<T>.Inner与以下类型不同Outer<T>.Inner.Inner. Outer<T>.Inner是一个子类Outer<Outer<T>.Inner> while Outer<T>.Inner.Inner是一个子类Outer<Outer<Outer<T>.Inner>.Inner>,我们之前建立的不同于Outer<T>.Inner. So Outer<T>.Inner.Inner and Outer<T>.Inner指的是不同的类型。

生成 IL 时,编译器始终使用类型的完全限定名称。您巧妙地找到了一种方法来引用名称长度以指数速度增长的类型。这就是为什么当你增加通用数量时Outer或添加额外级别.Y到田间地头field in Inner输出 IL 大小和编译时间增长得如此之快。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么泛型类中重复嵌套类型的字段声明会导致源代码大幅增加? 的相关文章

随机推荐