C#小测试(二):嵌套子类带来的困惑
2008-08-04 11:53
232 查看
这里有个很有意思的题目,先别运行程序,猜猜它会输出什么?
[code]{
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}这里的T是int、string还是其它的什么?还是程序压根儿就不能通过编译?
...
...
...
答案是System.Int32,你猜对了吗?很可能这跟你的期望值并不一致。我们来看看它背后的玄妙之处。
有人认为c.M方法输出Int32,因为B的声明B:A<int>告诉B,T将一直是int。这好像是有道理的,却不是真正的原因。我们先不管C,执行这一行代码(newA<string>.B()).M(),它会输出:String。B是A<int>的子类并不意味着B中的T总是int。
问题的根本在于声明classC:B会产生不明确的内容:即是classC:A<T>.B还是classC:A<int>.B?我们得首先搞清楚这个问题。
先看继承和嵌套类的区别:
[code]{
[/code]
...
(newY.Z()).M();//合法,M是继承自基类的方法
(newY.Z()).N();//不合法,外部类的成员不是内部类的方法
在我们的测试题中,我们调用了A<string>.B.C类的方法M,它本质上则调用了基类的M方法。如果基类是A<T>.B,那么应调用A<T>.B.M,即输出T的当前值:String;如果基类是A<int>.B,那么应调用A<int>.B.M,即总是输出:Int32。
而程序的结果告诉我们C#选择了后者。是不是有些不可思议?
也许程序中的泛型扰乱我们的直觉。那就看一个不使用泛型的例子:
[code]{
[/code]
OK,目前为止,一切都是合法的,而且也没什么异议。当我们通过类型名称来引用类型时,类型可以来自于基类(H.e),也可以来自于外部类(G.e)。但是如果这两种情况同时出现会如何呢?
[code]{
[/code]
这种情况是合法的吗?答案是肯定的。此时C#认为基类要优先于外部类。这也合乎常理,派生类与基类的关系是“is-a”,内部类与外部类的关系是“包含于”,前者要比后者更为紧密。
还可以这么理解:从基类继承下来的成员都属于“当前的作用域”,因此从“外部的作用域”获得的成员的优先级要低一些。
一般地,对于一个类型S,在其上下文中对一个名称进行解析的算法是:
S的类型参数(typeparameter)
S可以访问的内部类
S的基类中可以访问的内部类(访问基类的顺序是由近及远)
S的外部类
现在回到开始的问题。我们看看在解析C的基类的时候发生了什么。我们调用的是B.M(),所以问题可以转化为找到正确的B。首先C没有类型参数,也没有内部类。
A<T>.B的基类是A<int>,而外部类则是A<T>,两者都有一个名称为B的内部类。选哪一个呢?根据上面的算法,基类优先,即A<int>.B。
这个过程实在是非常的绕,虽然跟着文章得出了结论,还是有些模糊。。。
参考:
AnInheritancePuzzle,PartOne
AnInheritancePuzzle,PartTwo
publicclassA<T>
[code]{
publicclassB:A<int>
{
publicvoidM()
{
Console.WriteLine(typeof(T).ToString());
}
publicclassC:B{}
}
}
classProgram
{
staticvoidMain(string[]args)
{
A<string>.B.Cc=newA<string>.B.C();
c.M();
}
}
[/code]
.csharpcode,.csharpcodepre
{
font-size:small;
color:black;
font-family:consolas,"CourierNew",courier,monospace;
background-color:#ffffff;
/*white-space:pre;*/
}
.csharpcodepre{margin:0em;}
.csharpcode.rem{color:#008000;}
.csharpcode.kwrd{color:#0000ff;}
.csharpcode.str{color:#006080;}
.csharpcode.op{color:#0000c0;}
.csharpcode.preproc{color:#cc6633;}
.csharpcode.asp{background-color:#ffff00;}
.csharpcode.html{color:#800000;}
.csharpcode.attr{color:#ff0000;}
.csharpcode.alt
{
background-color:#f4f4f4;
width:100%;
margin:0em;
}
.csharpcode.lnum{color:#606060;}这里的T是int、string还是其它的什么?还是程序压根儿就不能通过编译?
...
...
...
答案是System.Int32,你猜对了吗?很可能这跟你的期望值并不一致。我们来看看它背后的玄妙之处。
有人认为c.M方法输出Int32,因为B的声明B:A<int>告诉B,T将一直是int。这好像是有道理的,却不是真正的原因。我们先不管C,执行这一行代码(newA<string>.B()).M(),它会输出:String。B是A<int>的子类并不意味着B中的T总是int。
问题的根本在于声明classC:B会产生不明确的内容:即是classC:A<T>.B还是classC:A<int>.B?我们得首先搞清楚这个问题。
先看继承和嵌套类的区别:
publicclassX
[code]{
publicvoidM(){}
}
publicclassY
{
publicvoidN(){}
publicclassZ:X{}
}
[/code]
...
(newY.Z()).M();//合法,M是继承自基类的方法
(newY.Z()).N();//不合法,外部类的成员不是内部类的方法
在我们的测试题中,我们调用了A<string>.B.C类的方法M,它本质上则调用了基类的M方法。如果基类是A<T>.B,那么应调用A<T>.B.M,即输出T的当前值:String;如果基类是A<int>.B,那么应调用A<int>.B.M,即总是输出:Int32。
而程序的结果告诉我们C#选择了后者。是不是有些不可思议?
也许程序中的泛型扰乱我们的直觉。那就看一个不使用泛型的例子:
publicclassD
[code]{
publicclassE{}
}
publicclassF
{
publicclassE{}
publicclassG
{
publicEe;//很明显这里是F.E
}
}
publicclassH:D
{
publicEe;//很明显这里是D.E
}
[/code]
OK,目前为止,一切都是合法的,而且也没什么异议。当我们通过类型名称来引用类型时,类型可以来自于基类(H.e),也可以来自于外部类(G.e)。但是如果这两种情况同时出现会如何呢?
publicclassJ
[code]{
publicclassE{}
publicclassK:D
{
publicEe;//是J.E还是D.E?
}
}
[/code]
这种情况是合法的吗?答案是肯定的。此时C#认为基类要优先于外部类。这也合乎常理,派生类与基类的关系是“is-a”,内部类与外部类的关系是“包含于”,前者要比后者更为紧密。
还可以这么理解:从基类继承下来的成员都属于“当前的作用域”,因此从“外部的作用域”获得的成员的优先级要低一些。
一般地,对于一个类型S,在其上下文中对一个名称进行解析的算法是:
S的类型参数(typeparameter)
S可以访问的内部类
S的基类中可以访问的内部类(访问基类的顺序是由近及远)
S的外部类
现在回到开始的问题。我们看看在解析C的基类的时候发生了什么。我们调用的是B.M(),所以问题可以转化为找到正确的B。首先C没有类型参数,也没有内部类。
A<T>.B的基类是A<int>,而外部类则是A<T>,两者都有一个名称为B的内部类。选哪一个呢?根据上面的算法,基类优先,即A<int>.B。
这个过程实在是非常的绕,虽然跟着文章得出了结论,还是有些模糊。。。
参考:
相关文章推荐
- C#基础-039 设计一个学生类Student和它的一个子类Undergraduate,并进行测试
- C# 类与类之间的嵌套,声明时子类对象为空的问题
- [C#]I/O完成端口的类定义和测试实例
- Oracle 性能测试一:嵌套SQL的查询速度比较分析(初级)
- C#/C++/CLI运行效率测试之二: Native C++调用Native C++ DLL
- C#中提供的精准测试程序运行时间的类Stopwatch
- C# 启动/停止 iis 网站 例子源码(iis 6.0下测试通过)
- C# 与 C 分别实现最简单的tcp-modbus 通信测试 (func_code = 0x03)
- 浅谈C# 4.0带来的新特性
- C#中提供的精准测试程序运行时间的类Stopwatch
- 关于C#测试Oracle数据库链接的问题
- C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试
- c#整人代码!!网络收集!!!(本代码没经过测试)
- 用C#实现实现简单的 Ping 的功能,用于测试网络是否已经联通
- C#中XML文档生成实例:Elevator Scheduler测试文件的生成详解
- C#创建COM组件,QT调用COM组件简单测试(VS2008+QT4.6.4)
- [收藏]WINFORM(C#)的键盘测试
- C# 生成多层嵌套JSON到数组中(树结构JSON)
- 使用嵌套 Repeater 控件和 Visual C# .Net 显示分层数据
- C# 写个公共的测试代码运行时间的方法