Effective C# 只有当新版基类导致问题时才考虑使用new修饰符

我们一般在类成员上使用new修饰符,来重新定义继承自基类的非虚成员。我们可以这么做并不意味着我们就应该这么做。重新定义非虚方法会导致含混不清的行为。例如,对于下面的代码,绝大多数开发人员都会不假思索地认为它们的行为是一样的(假设两个类有继承关系):

object  c  =  MakeObject( );

//  通过MyClass引用调用:

MyClass cl 
=  c  as  MyClass;

cl.MagicMethod( );

//  通过MyOtherClass引用调用:

MyOtherClass cl2 
=  c  as  MyOtherClass;

cl2.MagicMethod( );

 

如果使用了new修饰符,情况就不是这样了:

 

public   class  MyClass

ExpandedBlockStart.gifContractedBlock.gif
{

public void MagicMethod( )

ExpandedSubBlockStart.gifContractedSubBlock.gif
{

    
// 忽略细节。

}


}


public   class  MyOtherClass : MyClass

ExpandedBlockStart.gifContractedBlock.gif
{

// 重新定义MagicMethod。

public new void MagicMethod( )

ExpandedSubBlockStart.gifContractedSubBlock.gif
{

    
// 忽略细节。

}


}

 

这种做法会导致许多含混不清的地方。如果在同样的对象上调用同样的函数,我们期望同样的代码被执行。但事实是,如果我们更改了用来调用函数的引用,函数调用的行为也将有所不同。这种不一致的行为看上去很荒唐。一个MyOtherClass对象,由于对它的引用不同,而有不同的行为。修饰符new并不会将一个非虚方法变为一个虚方法。相反,它会在类中添加一个不同的方法。

        非虚方法为静态绑定。任何引用MyClass.MagicMethod()的源代码调用的都将是该方法。系统不会在运行时寻找派生类中定义的其他版本。另一方面,虚函数使用的是动态绑定。系统会根据对象的运行时类型来选择调用正确的函数。

        避免使用new修饰符来重定义非虚函数,并非意味着我们要将基类中所有的函数都定义为虚函数。当程序库的设计者将一个函数定义为虚函数时,实际上是为类型订立了一项合同:即表明任何派生类都可以更改虚函数的实现。事实上,虚函数集合定义了派生类中所有可能改变的行为。“默认为虚”的设计表明派生类可以更改类的所有行为。这意味着我们没有仔细思考派生类到底会更改哪些部分的行为。我们不应该这么做。相反,我们应该花费时间仔细考虑应该将哪些方法和属性声明为多态成员。我们应该仅将它们声明为虚成员。不要认为这种做法是对类的用户的限制。相反,应该将这种做法当作是在为定制类型行为提供一些入口点。

        仅有一种情况我们需要使用new修饰符,那就是在我们使用新版的基类后,其增添的方法名和子类中现在已经被使用的方法名冲突。因为已经有代码在依赖子类中现有的方法名了,比如可能有其他程序集在使用这样的方法。例如,我们通过继承另外一个程序库中定义的BaseWidget,定义了新的MyWidget类:

 

public   class  MyWidget : BaseWidget

ExpandedBlockStart.gifContractedBlock.gif
{

public void DoWidgetThings( )

ExpandedSubBlockStart.gifContractedSubBlock.gif
{

    
// 忽略细节。

}


}

 

假设我们完成了MyWidget之后,已经有客户在使用它。然后我们发现BaseWidget公司又发布了一个新版的BaseWidget。由于对其中的新功能抱有热切的期待,我们立即购买了它,并试图生成新版的MyWidget。可是,生成的时候失败了,原因在于BaseWidget添加了自己的DoWidgetThings方法。

 

public   class  BaseWidget

ExpandedBlockStart.gifContractedBlock.gif
{

public void DoWidgetThings()

ExpandedSubBlockStart.gifContractedSubBlock.gif
{

    
// 忽略细节。

}


}

 

这是一个问题,我们的基类悄无声息地在其内引入了一个和子类同名的方法。有两种修正该问题的方法。首先,我们可以更改DoWidgetThings方法的名字:

 

public   class  MyWidget : BaseWidget

ExpandedBlockStart.gifContractedBlock.gif
{

public void DoMyWidgetThings( )

ExpandedSubBlockStart.gifContractedSubBlock.gif
{

    
// 忽略细节。

}


}



        或者,我们可以使用new修饰符:

public   class  MyWidget : BaseWidget

ExpandedBlockStart.gifContractedBlock.gif
{

public new void DoWidgetThings( )

ExpandedSubBlockStart.gifContractedSubBlock.gif
{

    
// 忽略细节。

}


}

 

 

如果能访问到MyWidget类的所有客户程序代码,我们应该选择更改方法的名字,因为这在长期来讲比较方便。但是,如果我们的MyWidget类发行遍布全世界,那将迫使所有客户做繁多的更改。这就是 new修饰符的用武之地了。我们的客户可以继续使用DoWidgetThings()方法而无需做任何更改。他们也不会调用 BaseWidget.DoWidget- Things(),因为这样的调用在客户代码中不可能存在。修饰符new正是应用于这样的场合:新版的基类增添的成员与子类中先前已经声明的成员发生了冲突。

        当然,随着时间的推移,我们的用户可能也会试图去使用BaseWidget.DoWidget- Things()方法。这时候我们又回到了原来的问题上:两个方法看起来相同,但实际上不同。因此,我们应该考虑new修饰符所带来的长期不良影响。有时候,短期内更改方法名所导致的不方便可能仍然是值得的。

      综上所述,使用new修饰符必须小心。如果不分青红皂白地使用,便会在对象上出现含混不清的方法调用。只有在“新版的基类增添的成员与子类中已存在的成员发生了冲突”这样特殊的情况下,我们才应考虑使用 new修饰符。即使在这种情况下,在使用它之前我们也要慎重考虑。除此之外,我们不应该再在任何其他情况下使用new修饰符。

转载于:https://www.cnblogs.com/VisualStudio/archive/2008/11/02/1324656.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
水资源是人类社会的宝贵财富,在生活、工农业生产中是不可缺少的。随着世界人口的增长及工农业生产的发展,需水量也在日益增长,水已经变得比以往任何候都要珍贵。但是,由于人类的生产和生活,导致水体的污染,水质恶化,使有限的水资源更加紧张。长期以来,油类物质(石油类物质和动植物油)一直是水和土壤中的重要污染源。它不仅对人的身体健康带来极大危害,而且使水质恶化,严重破坏水体生态平衡。因此各国都加强了油类物质对水体和土壤的污染的治理。对于水中油含量的检测,我国处于落后阶段,与国际先进水平存在差距,所以难以满足当今技术水平的要求。为了取得具有代表性的正确数据,使分析数据具有与现代测试技术水平相应的准确性和先进性,不断提高分析成果的可比性和应用效果,检测的方法和仪器是非常重要的。只有保证了这两方面才能保证快速和准确地测量出水中油类污染物含量,以达到保护和治理水污染的目的。开展水中油污染检测方法、技术和检测设备的研究,是提高水污染检测的一条重要措施。通过本课题的研究,探索出一套适合我国国情的水质污染现场检测技术和检测设备,具有广泛的应用前景和科学研究价值。 本课题针对我国水体的油污染,探索一套检测油污染的可行方案和方法,利用非分散红外光度法技术,开发研制具有自主知识产权的适合国情的适于野外便携式的测油仪。利用此仪器,可以检测出被测水样中亚甲、甲物质和动植物油脂的污染物含量,为我国众多的环境检测站点监测水体的油污染状况提供依据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值