Ily*_*nov 39 .net c# generics nested c#-4.0
场景非常罕见,但非常简单:定义一个泛型类,然后创建一个嵌套类,它继承自外部类并在嵌套中定义一个关联字段(self类型).代码片段比描述更简单:
class Outer<T>
{
class Inner : Outer<Inner>
{
Inner field;
}
}
Run Code Online (Sandbox Code Playgroud)
在反编译IL之后,C#代码如下所示:
internal class Outer<T>
{
private class Inner : Outer<Outer<T>.Inner>
{
private Outer<Outer<T>.Inner>.Inner field;
}
}
Run Code Online (Sandbox Code Playgroud)
这看起来很公平,但是当你改变字段的类型声明时,事情变得更加棘手.所以当我将字段声明更改为
Inner.Inner field;
Run Code Online (Sandbox Code Playgroud)
反编译后,此字段将如下所示:
private Outer<Outer<Outer<T>.Inner>.Inner>.Inner field;
Run Code Online (Sandbox Code Playgroud)
我明白,这个类'嵌套'和继承彼此并不相处,但为什么我们会观察到这种行为呢?在 Inner.Inner 类型声明已经发生任何变化的类型?是 Inner.Inner 和 Inner 类型,在此情况下某种方式有什么不同?
您可以在下面看到该类的反编译源代码.它非常庞大,总长度为12159个符号.
class X<A, B, C>
{
class Y : X<Y, Y, Y>
{
Y.Y.Y.Y.Y.Y y;
}
}
Run Code Online (Sandbox Code Playgroud)
最后,这堂课:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
导致27.9 MB (29,302,272 bytes)装配和Total build time: 00:43.619
编译在C#5和C#4编译器下完成.反编译由dotPeek完成.构建配置:Release和Debug
Mik*_*ray 25
你的问题的核心是为什么Inner.Inner不同的类型Inner.一旦理解了这一点,您对编译时间和生成的IL代码大小的观察就会很容易.
首先要注意的是,当你有这个声明时
public class X<T>
{
public class Y { }
}
Run Code Online (Sandbox Code Playgroud)
与名称相关的无限多种类型Y.每个泛型类型参数都有一个T,因此X<int>.Y不同于X<object>.Y以后的重要X<X<T>>.Y类型,而不是X<T>.Y所有类型T.您可以针对各种类型进行测试T.
接下来要注意的是
public class A
{
public class B : A { }
}
Run Code Online (Sandbox Code Playgroud)
有很多种方法可以引用嵌套类型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.所以Outer<T>.Inner.Inner,Outer<T>.Inner指的是不同的类型.
生成IL时,编译器始终使用类型的完全限定名称.您巧妙地找到了一种方法来引用名称长度以指数速率增长的类型.这就是为什么当您在输出IL大小中Outer增加.Y字段的通用性或向字段添加其他级别时,编译时间会快速增长.fieldInner
这不是答案!
你的问题有很多方面.一个小问题是:如果一个类型包含(因为继承,否则不允许)一个与类型本身同名的嵌套类型,如果在该类型中使用该名称,该名称引用什么?
用文字表达很难,但这是我心中的例子:
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?
}
}
Run Code Online (Sandbox Code Playgroud)
注意:这很简单!没有泛型!继承自己的包含类的类没有问题.
这里的问题是,什么类型的g?它N.Giraffe(一个类),还是它N.Giraffe.Giraffe(一个接口)?正确答案是后者.因为要找到名称的含义Giraffe,首先要搜索当前类型的成员(在这种情况下,查找接口).只有在那里找不到匹配时,才会进入当前命名空间的成员(将找到当前类型).
从使用当前类型参数化的泛型继承通常称为奇怪的重复模板模式,并且之前在C#编译器团队中的Eric Lippert不鼓励这种模式.
在您的情况下,嵌套类Outer<A>.Inner被定义为继承自Outer<Inner>.这意味着嵌套类通过继承包含嵌套类的定义.这产生了嵌套类的无限定义:Outer<A>.Inner.Inner.Inner...
现在,在您的原始定义中
class Inner : Outer<Inner>
{
Inner field;
}
Run Code Online (Sandbox Code Playgroud)
该字段被声明为Inner在此范围内引用当前类型的类型.当您将其更改为时Inner.Inner,第一个Inner引用当前正在定义的类型,第二个.Inner引用通过继承获得的嵌套类.
作为一个例子,让我们扩展Inner类的定义,以包含继承的内容(并重命名以使事情更清晰):
//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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
回到你的问题:
为什么我们观察到这种行为?Inner.Inner类型声明是否已更改类型?在这种情况下,Inner.Inner和Inner类型在某些方面有所不同吗?
您更改了类Inner的类型定义,以使其字段具有不同的类型.Outer<A>.Inner可能(我尚未验证)的实例可以转换为其他内部类型,但它们是2种类型定义.
| 归档时间: |
|
| 查看次数: |
1435 次 |
| 最近记录: |