关于C#一个基础问题的初步探查

关于C#一个弱弱问题的初步探查

在书上看到这样一个例子:

已知:
interface IControl
{
void Paint();
}
class Control : IControl
{
public void Paint(){ ... }
}
class TextBox : Control
{
new public void Paint(){ ... }
}

问:
IControl it = new TextBox();
it.Paint(); //调用哪个Paint()?

书上的结论是Control::Paint(),并且吹了一通什么没有virtual关键字或者让TextBox直接继承至IControl就是静态决议,什么如果Control::Paint()加上virtual又怎么怎么,如果TextBox::Paint()去掉new又怎么怎么,感觉甚是繁琐、复杂和多变,让人如坠万丈深渊,万劫不复…(- -)

就我原来从C++带来的理解:IControl是个interface,其实隐含IControl::Paint()是abstract 以及 public,只是不能显式写出;所以成员函数的调用决议应该是运行期完成,也就是说为TextBox::Paint();

实际上机测试结果,令人汗颜,他的结果的确是正确的,但是他的后续所有解释都是错误的;不管如何加virtual、去掉new,最后的结果都是一个—Control::Paint(),程序代码如下:
using System;
namespace TestInterface
{
interface IControl
{
void Paint();
}
class Control : IControl
{
public void Paint()
{
Console.WriteLine("Control::Paint()");
}
}
///

/// TextBox 的摘要说明。
///

class TextBox : Control
{
new public void Paint()
{
Console.WriteLine("TextBox::Paint()");
}
}
///

/// TestInterface 的摘要说明。
///

class TestInterface
{
///

/// 应用程序的主入口点。
///

[STAThread]
static void Main(string[] args)
{
//
// TODO: 在此处添加代码以启动应用程序
//
IControl it = new TextBox();
it.Paint();
}
}
}

如果去掉new当然仅仅会导致警告,实际上还是覆盖。

书中还提到“我们的误解来自一个假设:Control::Paint()被自动视为vitrual”,根据实际上机测试,这个假设的确是成立的,而不是什么误解,至于加上virtual和去掉virtual所带来的区别仅仅在于是否加入关键字final。如果没有加上了virtual,那么Control::Paint()会自动视为final(IL中),有例为证:

Contorl::Paint()
.method public hidebysig newslot virtual final //如果加上vitrual则这里去掉final
instance void Paint() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "Control::Paint()"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Control::Paint
TextBox::Paint()
.method public hidebysig instance void Paint() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "TextBox::Paint()"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method TextBox::Paint

而在Main()中仅是简单调用IControl::Paint()

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
// 代码大小 13 (0xd)
.maxstack 1
.locals init ([0] class TestInterface.IControl it)
IL_0000: newobj instance void TestInterface.TextBox::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: callvirt instance void TestInterface.IControl::Paint()
IL_000c: ret
} // end of method TestInterface::Main

===== 华丽的分割线 =====
呵呵,以上就是我初步的分析,特别混乱,不要见笑,下面我做个总结(个人意见、仅供参考)

原来的C++到C#,在函数重载这一块有一个很明显的区别,那就是override关键字。如果重载必须显式的指明override关键字,如果没有,将隐藏基类成员。这点是很自然的,但是本例的特别之处在于interface的引入。

—– interface —–
interface默认为abstract类,但是和普通abstract类不同的是,他并没有派生自System.Object;其内接口也默认为abstract virtual如下:

.class interface private abstract auto ansi IControl
{
} // end of class IControl
.method public hidebysig newslot abstract virtual
instance void Paint() cil managed
{
} // end of method IControl::Paint

之所以interface被默认为public(指其内接口,而interface本身可以不为public),abstract就不用解释了,但是明示出来反而会出错;
—– interface end —–

—– interface implement —–
.class private auto ansi beforefieldinit Control
extends [mscorlib]System.Object
implements TestInterface.IControl
{
} // end of class Control
.method public hidebysig newslot virtual final //如果加上vitrual则这里去掉final
instance void Paint() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "Control::Paint()"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Control::Paint

这里太有趣了(- -,过分了…还是说稍微有点意思)interface接口的实现会在IL中自动加上virtual关键字,而不像刚才书上说的,什么误解,这是为什么呢?因为如果像本例中一样,用IControl调用Paint(),而IControl本身是不会实现Paint()的,所以他的实现必须在IL中以virtual关键字标明,让动态决议的时候,能够考虑到这个实现,不然IControl::Paint()的调用就会出问题。

至于这里提到的final,也是interface implement比较独特的一点。override会使得本身重载基类成员,并且本身成为virtual,所以自己的子类也可以重载自己。在程序中,若想重载基类成员,但又不想让子类继续重载,就会使用override sealed关键字,这里IL中的final关键字实际上就是指的sealed。

由此,我们一般写一个函数:

public void foo();

子类都无法override,除非函数改为:

virtual void foo();

但是这里由于interface implement本身需要申明为override(IL中仍为virtual),所以为了延续惯例,用sealed(IL中为final)封闭之,除非你显示的指示virtual,这样就和我们通常的观念保持一致鸟~~~~
—– interface implement end —–

—– extended classes —–
.class private auto ansi beforefieldinit TextBox
extends TestInterface.Control
{
} // end of class TextBox
extends TestInterface.Control
.method public hidebysig instance void Paint() cil managed
{
// 代码大小 11 (0xb)
.maxstack 1
IL_0000: ldstr "TextBox::Paint()"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method TextBox::Paint

这里不管你是否采用new关键字,实际上都起到了覆盖的作用,只是如果没有new关键字会引发警告,提醒你是否确实是想覆盖,如果你想关闭警告,就必须显示的告诉编译器我想覆盖—就是使用new关键字。

值得一提的有两点:
1、如果程序中直接写virtual而不用override,其实还是覆盖。可以从IL中看出:
程序<->IL
virtual<->newslot virtual
override<->virtual
2、如果这个类直接多重继承IControl,像这样:
class TextBox : Control, IControl
{
...
}

实际上这个类就不再属于extended classes了,而是interface implement。由于对于interface implement的选择是virtual的,所以很自然会调用TextBox::Paint();
—– extended classes end —–

如果以上的讨论正确,本例就很好解释鸟~~~

IControl::Paint()由于是virtual,他会先行寻找可行函数,再进行最佳决议;

由于new关键字的覆盖函数并未override,所以他连可行函数都算不上。自然无论如何都不可能被调用。相反,可能函数只有Control::Paint();

如果让TextBox::Paint()用override修饰(自然Control::Paint()需要用virtual修饰),那么可能函数有Control::Paint(),TextBox.Paint(),其中根据动态决议,最佳为TextBox.Paint();

我ZTMD是大学生,TMD人格担保

时间:2005-04-14 11:52
地点:风华食堂后的厕所(男)
地点介绍:
此厕所已渐渐成为网管会专用厕所。由于有动手能力非常强的食堂专业清洁人员打扫(2次/天),加之那个3分钟一冲的变态冲水箱,所以整个厕所给人的感觉明亮、清洁、无异味,实为大小便、特别大便…
人物:
我(以下简称W,所述均为心理活动)、神秘人物A、神秘人物B
人物对白:
A:呵呵,今天来体会一下大学厕所…

(W:…)

B:不是吧,这里是食堂的厕所,不是大学生用的吧

(W:…)

A:哎呀,无所谓嘛,反正是在大学里面;你没看到刚才有个大学生阿(最后这个分句用的是气声,但我还是听到了,ZTMD听力好阿,没办法…)

(W:仔细听,好像确实厕所里除了我和他们应该没别人了,暴寒,开始理卫生纸,准备撤退…)

A:咦,你改小的阿,那你在外头等哈,我要大

B:其实你一说我也有点想大了

(W:加速整理卫生纸,无奈最近饮食无规律,加之惊吓过度,小腹痉挛,剧痛ing…)

A:那就一起嘛

(W:…)

旁白:打击乐?哦,不是,那是气流的声音…

(W:加速、加速…)

旁白:背景音乐起?哦,不是,那是手机铃声

B:喂,喂,听得到,你说嘛,啊,啊…(限于篇幅,省略若干)随便嘛,你们看到办,我正在忙…

(W:湿了,湿了…我的意思是我出汗了)

A:你小声一点嘛,拉个X都不清静,声音又大还要打手机(B:马上就完,马上,马上…)你看别个大学生,素质就是不一样,拉个X安安静静的…

B:不要乱说,别个听的到…(用的是气声…我的听力优势啊…呜呜)啊,啊,啊,晓得了,刚才不是说你哈…(对手机说…)

A:啊哈哈,搞忘了(气声…)同学,对不起哈,打搅了,你慢慢…

(W:咦,我为啥子在厕所外面了呢?呵呵,我的擦PP的速度还是很惊人的啊…小腹剧痛ing…)

ZTMD宁愿重写,也不排错

奋战了一个星期的简易登陆系统排错,最后的结果让人汗颜:
将Web.Config里面的
<authentication mode="Windows">
</authentication>

改为
<authentication mode="Forms">
<forms name="TheSky" path="/" loginUrl="/TheSky/WebModules/Users/Login.aspx" protection="All" timeout="30">
</forms>
</authentication>

我一直没有查Web.Config。唉,已经欲哭无泪了,呜呜…

ps:ztmd,ztmD,ztMd,zTmd,Ztmd,ztMD,zTmD,ZtmD,zTMd,ZtMd,ZTmd,zTMD,ZtMD,ZTmD,ZTMd,ZTMD

关于Gmail怨念的附件下载

在Gmail下载了马莞迪的一个附件,同时生成了两个文件,CAG6D4B.和CAG6D4B,改任意一个后缀为DOC后正常浏览,后删除二者,结果只删掉一个,还剩下了一个CAG6D4B.,说找不到文件…

于是怨念的旅程便开始了,先试了各种方法删除之:

在win下查看属性,隐藏和只读,分别改之,提示找不到文件

Dos下:

del /F /ARH ...提示找不到文件
Attrib -rh ...提示找不到文件
ren ... 提示找不到文件

现在意识到问题的严重性,而且听说季翔也不幸中招…..上网查资料,各大Blog,论坛很多地方有和我一样的问题,但是没有任何有用的回复(有人说安全模式下可以删除,结果小白的跑去试,当然不行,提示找不到文件)

接着采用原来屡试不爽的杀手锏:

del. file-pathfile-name... 提示找不到文件

当然再接下来就是国外的网站,因为感觉这些BT的咚咚老外老是厉害一点(应该不算崇洋媚外吧)结果发现根本没有这个问题的内容(后来才发现此问题大概只出现在Gmail处理中文附件的时候,所以老外大概没有机会碰上吧)

至此我已彻底无语,剩下的路不多了:

配ServU将路径指向桌面,然后用FTPRush删除之,提示成功,但没有删除

又发现国内某论坛有人对此有所深入探究,引用如下:

“我在同目录里(桌面)又建立了一个名叫“CAJUWNF5”的文件,名字一样但没有那个点儿。再删除原先那个“CAJUWNF5.”时,轻松成功。然而,新建立这个“CAJUWNF5”却删不掉了。现在它的文件名里没有了点,仍删不掉,也照样不能改名或移动。 ”

唉,他这个想法就比较BT,不过还是没有解决问题,但是倒让人很生疑惑,文中他还提到:

“更糟的是,我又重复了一次原先的操作(从gmail中保存附件),想看看是怎么回事。现在,我的桌面上有两块橡皮膏了。”(我也是这样…)

“毛病出在文件名末尾的“.”上。似乎这个点让NTFS在判别文件时产生了什么循环,以致找不到文件。要是FAT,碰到这种文件,用扇区读写工具(比如 debug)进入分区表,手工就能解决。不知NTFS有没有类似的办法。 ”(想来要是FAT,我也不会用debug改分区表…)

本来想就此打住,上各大论坛发帖询问,结果突然看见了任务栏上196的远程SecureCRT连接,想象要是在linux下用rm -rf说不定可以干掉,说干就干:

配共享,开防火墙(天网,Microsoft,IBM)终于在196上smbclient -L 202.115.22.*看到了桌面的共享,然后rm -rf …轻松干掉

有的时候还是感觉linux好阿