|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
兄弟们,想来你们都看过了昨天的比赛了。我现在的痛苦状跟当时应该差不多。希望本版.net老师不吝赐教,为小弟这一批迷途的羊羔指一条阳光之道!您也知道:学习技术如果只有一个人摸索,那是一件多么痛苦的事情!还有,如果万辛能得名师或长者指点,那又是多么一件幸福和快乐的事情!编程|关头字 1.你一般如何用多态?
假定我有一个类,内里有一个PrintStatus办法,用于打印实例确当前形态,我但愿该类的派生类都带有一个PrintStatus办法,而且这些办法都用于打印实在例确当前形态。那末我会如许表达我的希望:
//Code#01
classBase
{
publicvirtualvoidPrintStatus()
{
Console.WriteLine("publicvirtualvoidPrintStatus()inBase");
}
}
因而我能够写一个如许的办法:
//Code#02
publicvoidDisplayStatusOf(Base[]bs)
{
foreach(Basebinbs)
{
b.PrintStatus();
}
}
bs中大概包括着分歧的Base的派生类,但我们却能够疏忽这些“本性”而利用一种一致的体例来处置某事。在.NET2.0中,XmlReader的Create有如许一个版本:
publicstaticXmlReaderCreate(Streaminput);
你能够向Create传送任何可用的“流”,比方来自文件的“流”(FileStream)、来自内存的“流”(MemoryStream)或来自收集的“流”(NetworkStream)等。固然每中“流”的事情细节都分歧,但我们却利用一种一致的体例来处置这些“流”。
2.假设有人不恪守答应...
DisplayStatusOf隐含着如许一个假定:bs中假如存在派生类的实例,那末该派生类应当重写PrintStatus,固然必需加上override关头字:
//Code#03
classDerived1:Base
{
publicoverridevoidPrintStatus()
{
Console.WriteLine("publicoverridevoidPrintStatus()inDerived1");
}
}
你能够把这看做一种答应、商定,直到有人沉不住气...
//Code#04
classDerived2:Base
{
publicnewvoidPrintStatus()
{
Console.WriteLine("publicnewvoidPrintStatus()inDerived2");
}
}
假定我们有如许一个数组://Code#05
Base[]bs=newBase[]
{
newBase(),
newDerived1(),
newDerived2()
};
把它传送给DisplayStatusOf,则输入是:
//Output#01
//publicvirtualvoidPrintStatus()inBase
//publicoverridevoidPrintStatus()inDerived1
//publicvirtualvoidPrintStatus()inBase
从输入了局中很简单看出Derived2并没有依照我们希冀的往做。但你无需惊奇,这是因为Derived2的计划者没有“恪守商定”的原因。
3.new:封印咒术
new仿佛给人一种如许的感到,它的利用者喜好冲破他人的商定,但是,假如利用得当,new能够填补基类计划者的“短见”。在CreatingaDataBoundListViewControl中,RockfordLhotka就树模了怎样封印本来的ListView.Columns,并使自行增加的前往DataColumnHeaderCollection的Columns取而代之。
从Output#01中我们能够看到,new只是把Base.PrintStatus封印起来而不是没落失落,你能够排除封印然落后行会见。关于Derived2的利用者,解封的办法是把Derived2的实例转换成Base范例:
//Code#06
Based2=newDerived2();
d2.PrintStatus();
//Output#02
//publicvirtualvoidPrintStatus()inBase
而在Derived2外部,你能够透过base来会见:
//Code#07
base.PrintStatus();
这类办法是针对实例成员的,假如被封印的成员是静态成员的话,就要透过类名来会见了。
4.假设Base.PrintStatus是某个接口的隐式完成...
假设Base完成了一个IFace接口:
//Code#08
interfaceIFace
{
voidPrintStatus();
}
classBase:IFace
{
publicvirtualvoidPrintStatus()
{
Console.WriteLine("publicvirtualvoidPrintStatus()inBase");
}
}
我们只必要让Derived2从头完成IFace:
//Code#09
classDerived2:Base,IFace
{
publicnewvoidPrintStatus()
{
Console.WriteLine("publicnewvoidPrintStatus()inDerived2");
}
}
Derived1坚持稳定。则把:
//Code#10
IFace[]fs=newIFace[]
{
newBase(),
newDerived1(),
newDerived2(),
}
传送给:
//Code#11
publicvoidDisplayStatusOf(IFace[]fs)
{
foreach(IFacefinfs)
{
f.PrintStatus();
}
}
输入了局是:
//Output#03
//publicvirtualvoidPrintStatus()inBase
//publicoverridevoidPrintStatus()inDerived1
//publicnewvoidPrintStatus()inDerived2
从输入了局中,我们能够看到,固然Derived2.PrintStatus使用了new,但却仍然介入静态绑定,这是因为new只能切断Derived2.PrintStatus和Base.PrintStatus的接洽,而不克不及切断它与IFace.PrintStatus的接洽。我在Derived2的界说中从头指定完成IFace,这将使得编译器以为Derived2.PrintStatus是IFace.PrintStatus的隐式完成,因而,在静态绑准时Derived2.PrintStatus就被包含出去了。
<P> 5.谁的成绩?
我必需指出,假如Base(Code#01)和Derived2(Code#04)同时存在的话,它们俩个中一个存在着计划上的成绩。为何如许说呢?Base的计划者在PrintStatus上使用virtual申明了他但愿派生类能透太重写这一办法来介入静态绑定,即多态性;而Derived2的计划者在PrintStatus上使用new则申明了他但愿切断Derived2.PrintStatus和Base.PrintStatus之间的接洽,这将使得Derived2.PrintStatus没法介入到Base的计划者所希冀的静态绑定中。假如在Base.PrintStatus上使用virtual(即对多态性的希冀)是公道的话,那末Derived2.PrintStatus应当换用别的一个名字了;假如在Derived2.PrintStatus上使用new(即反对介入静态绑定)是公道的,那末Base.PrintStatus应当思索是不是往失落virtual了,不然就会呈现一些奇异的举动,比方Output#01的第三行输入。
假设承继系统中多态性举动的希冀是公道的话,那末更实践的做法应当是把Base界说成如许:
//Code#12
abstractclassBase
{
publicabstractvoidPrintStatus();
}
而本来Base中的完成应当下移到一个派生类中://Code#13
classDerived3:Base
{
publicoverridevoidPrintStatus()
{
Console.WriteLine("publicoverridevoidPrintStatus()inDerived3[originallyimplementedinBase]");
}
}
如许,Derived2.PrintStatus将使得编译没法完成,从而迫使其计划者要末变动办法的名字,要末换用override润色。这类强迫使得Derived2的计划者不能不从头思索其计划的公道性。
假设承继系统中多态性举动的希冀不老是公道呢?比方Stream有如许一个办法:
publicabstractlongSeek(longoffset,SeekOriginorigin);
如今假定我有一个办法在处置输出流时必要用到Stream.Seek:
//Code#14
publicvoidResume(Streaminput,longoffset)
{
//
input.Seek(offset,SeekOrigin.Begin);
//
}
当我们向Resume传送一个NetworkStream的实例,Resume将会抛出一个NotSupportedException,由于NetworkStream不撑持Seek。那末这是不是申明Stream的计划有成绩呢?
假想Resume是一个下载工具举行断点续传的办法,但是,并非一切的服务器都撑持断点续传的,因而,你必要起首判别输出流是不是撑持Seek操纵,再决意怎样处置输出流:
//Code#15
publicvoidResume(Streaminput,longoffset)
{
if(input.CanSeek)
{
//
input.Seek(offset,SeekOrigin.Begin);
//
}
else
{
//
}
}
假如CanSeek为false,那就只好重新来过了。
实践上,我们其实不能包管任何Stream的派生类都可以撑持某个(些)操纵,我们乃至不克不及包管来自统一个派生类的一切实例都撑持某个(些)操纵。你能够假想有如许一个PriorityStream,它可以依据以后登录账号的权限来决意是不是供应写操纵,这使得具有充足权限的人才网能修正数据。也许Stream的计划者已意料到这类情形的产生,以是CanRead、CanSeek和CanWrite就被到场到Stream里了。
值得注重的是,Code#07的Derived2多是一个很糟的计划,也多是一个很有用的计划。在本文,它是一个很糟的计划,假如你充足仔细,你会发觉到Derived2的计划者但愿Derived2.PrintStatus绕过Base.PrintStatus而间接和IFace.PrintStauts举行联系关系,外表上这没甚么不当,但本色上Base.PrintStatus和IFace.PrintStauts在商定上是同质的,这意味着假如与IFace.PrintStauts举行联系关系就即是供认本人和Base.PrintStatus是同质的,如许的话,为何不间接在Derived2里重写PrintStatus呢?在《基类与接口夹杂承继的声明成绩》中,我树模了一个有用的计划,用new和接口从头完成(Interfacereimplementation)来改正非预期的多态举动。
6.最初...
当我的伴侣拿着成绩来找我时,我一般都不会间接给出我的谜底,而是尽我的才能向他供应充足多的可用信息,以便他可以依据他所面对的实践情形作出处置,究竟,我不会比他更懂得他的成绩,而他也应当构成他本人的关于他的成绩的思索。我但愿荡子能用本人的谜底回覆他所提出的成绩,由于只要如许,那些常识才真正属于他,而且我也信任本文已供应了充足多的可用信息。兄弟们,想来你们都看过了昨天的比赛了。我现在的痛苦状跟当时应该差不多。希望本版.net老师不吝赐教,为小弟这一批迷途的羊羔指一条阳光之道!您也知道:学习技术如果只有一个人摸索,那是一件多么痛苦的事情!还有,如果万辛能得名师或长者指点,那又是多么一件幸福和快乐的事情! |
|