为什么在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: Release
和Debug
你的问题的核心是为什么Inner.Inner
是一个不同于Inner
types。 一旦你明白了,你对编译时间和生成的IL代码大小的观察很容易。
首先要注意的是,当你有这个声明
public class X<T> { public class Y { } }
与名称Y
相关的types有无数种。 每个genericstypes参数T
都有一个,所以X<int>.Y
与X<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.Inner
types与Outer<T>.Inner.Inner
。 Outer<T>.Inner
Outer<T>.Inner.Inner
是Outer<T>.Inner.Inner
的子类,而Outer<T>.Inner.Inner
是Outer<Outer<Outer<T>.Inner>.Inner>
Outer<T>.Inner.Inner
的子类以前和Outer<T>.Inner
不一样。 所以Outer<T>.Inner.Inner
和Outer<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定义。