为什么在generics类中重复嵌套types的字段声明会导致巨大的源代码增加?

场景是非常罕见的,但很简单:您定义了一个generics类,然后创build一个嵌套类,它从外部类inheritance,并在嵌套内定义一个关联字段(自我types)。 代码片段比描述更简单:

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

这似乎是公平的,但是当你改变字段的types声明时,事情变得更加棘手。 所以当我更改字段声明

 Inner.Inner field; 

反编译之后,这个字段将如下所示:

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

我明白,这个阶级的“嵌套”和inheritance不太相称,但是我们为什么要观察这样的行为呢? Inner.Inner types声明已经改变了types? Inner.Inner Inner types在这种情况下有所不同?

当事情变得非常棘手

您可以在下面看到反编译的源代码 。 它真的很大,总共有12159个符号。

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

最后,这个class级:

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

结果在27.9 MB (29,302,272 bytes)程序集和Total build time: 00:43.619

使用的工具

编译在C#5和C#4编译器下完成。 反编译由dotPeek完成。 构buildconfiguration: ReleaseDebug

你的问题的核心是为什么Inner.Inner是一个不同于Innertypes。 一旦你明白了,你对编译时间和生成的IL代码大小的观察很容易。

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

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

与名称Y相关的types有无数种。 每个genericstypes参数T都有一个,所以X<int>.YX<object>.Y ,后面X<X<T>>.Y是重要的, X<X<T>>.Y是与X<T>.Y不同的typesX<T>.Y所有的T都是。 您可以对各种types的T进行testing。

接下来要注意的是,

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

有无数种方法来引用嵌套typesB 一个是AB ,另一个是ABB ,等等。 typeof(AB) == typeof(ABB)的语句types返回true

当你把这两个方法结合起来的时候,会发生一些有趣的事情。 Outer<T>.Inner.Innertypes与Outer<T>.Inner.InnerOuter<T>.Inner Outer<T>.Inner.InnerOuter<T>.Inner.Inner的子类,而Outer<T>.Inner.InnerOuter<Outer<Outer<T>.Inner>.Inner> Outer<T>.Inner.Inner的子类以前和Outer<T>.Inner不一样。 所以Outer<T>.Inner.InnerOuter<T>.Inner.Inner指的是不同的types。

在生成IL时,编译器始终使用完全限定名称作为types。 您巧妙地find了一种方法来引用名称长度以指数速率增长的types。 这就是为什么当你增加Outer的通用.Y或者增加额外的等级.Y到内部的字段field时,输出IL的大小和编译时间增长得如此之快。

这不是一个答案!

你的问题有很多方面。 有一点是:如果一个types包含(因为inheritance,否则不允许)与types本身具有相同名称的嵌套types,如果该名称在types内部使用,名称是指什么?

这很难用言语expression,但这里是我想到的例子:

 namespace N { class Mammal { // contains nested type of an unfortunate name internal interface Giraffe { } } class Giraffe : Mammal { Giraffe g; // what's the fully qualified name of the type of g? } } 

注意:这很简单! 没有generics! 一个类inheritance自己的包含类没有问题。

这里的问题是, g的types是什么? 是N.Giraffe (一个类),还是N.Giraffe.Giraffe (一个接口)? 正确的答案是后者。 因为要find名字Giraffe的意思,首先search当前types的成员(在这种情况下可以find接口)。 只有在没有find匹配的情况下,才会进入当前命名空间的成员(当前types将被find)。

inheritance自用当前types参数化的generics通常被称为奇怪的循环模板模式,并且被以前在C#编译器团队中的Eric Lippert所阻碍。

在你的情况下,嵌套类Outer<A>.Inner被定义为从Outer<Inner>inheritance。 这意味着嵌套类通过inheritance来包含一个嵌套类的定义。 这产生了嵌套类的无限定义: Outer<A>.Inner.Inner.Inner...

现在,在你原来的定义

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

该字段被声明为typesInner ,在此范围内指的是正在定义的当前types。 当您将其更改为Inner.Inner ,第一个Inner引用当前定义的types,第二个.Inner引用通过inheritance获得的嵌套类。

作为一个例子,让我们扩展Inner类的定义,以包含来自inheritance的内容(重命名使事情变得更清晰):

 //original class Outer<A> { class Inner1 //: Outer<Inner1> { Inner1 field; //from inheritance class Inner2 : Outer<Inner1.Inner2> { Inner2 field; } } } //modified for Inner.Inner class Outer<A> { class Inner1 //: Outer<Inner1> { Inner1.Inner2 field; //from inheritance class Inner2 : Outer<Inner1.Inner2> { Inner2.Inner3 field; } } } 

所以回到你的问题:

我们为什么要观察这样的行为? Inner.Innertypes声明已经改变了types? Inner.Inner和Innertypes在这种情况下有所不同?

您更改了类Inner的types定义,以便其字段具有不同的types。 Outer<A>.Inner一个实例可能(我没有validation)可以转换为其他内部types,但是它们是2个types定义。