Effective C# 只有当新版基类导致问题时才考虑使用new修饰符
// 通过MyClass引用调用:
MyClass cl = c as MyClass;
cl.MagicMethod( );
// 通过MyOtherClass引用调用:
MyOtherClass cl2 = c as MyOtherClass;
cl2.MagicMethod( );
如果使用了new修饰符,情况就不是这样了:
{
public void MagicMethod( )
{
// 忽略细节。
}
}
public class MyOtherClass : MyClass
{
// 重新定义MagicMethod。
public new void MagicMethod( )
{
// 忽略细节。
}
}
这种做法会导致许多含混不清的地方。如果在同样的对象上调用同样的函数,我们期望同样的代码被执行。但事实是,如果我们更改了用来调用函数的引用,函数调用的行为也将有所不同。这种不一致的行为看上去很荒唐。一个MyOtherClass对象,由于对它的引用不同,而有不同的行为。修饰符new并不会将一个非虚方法变为一个虚方法。相反,它会在类中添加一个不同的方法。
非虚方法为静态绑定。任何引用MyClass.MagicMethod()的源代码调用的都将是该方法。系统不会在运行时寻找派生类中定义的其他版本。另一方面,虚函数使用的是动态绑定。系统会根据对象的运行时类型来选择调用正确的函数。
避免使用new修饰符来重定义非虚函数,并非意味着我们要将基类中所有的函数都定义为虚函数。当程序库的设计者将一个函数定义为虚函数时,实际上是为类型订立了一项合同:即表明任何派生类都可以更改虚函数的实现。事实上,虚函数集合定义了派生类中所有可能改变的行为。“默认为虚”的设计表明派生类可以更改类的所有行为。这意味着我们没有仔细思考派生类到底会更改哪些部分的行为。我们不应该这么做。相反,我们应该花费时间仔细考虑应该将哪些方法和属性声明为多态成员。我们应该仅将它们声明为虚成员。不要认为这种做法是对类的用户的限制。相反,应该将这种做法当作是在为定制类型行为提供一些入口点。
仅有一种情况我们需要使用new修饰符,那就是在我们使用新版的基类后,其增添的方法名和子类中现在已经被使用的方法名冲突。因为已经有代码在依赖子类中现有的方法名了,比如可能有其他程序集在使用这样的方法。例如,我们通过继承另外一个程序库中定义的BaseWidget,定义了新的MyWidget类:
{
public void DoWidgetThings( )
{
// 忽略细节。
}
}
假设我们完成了MyWidget之后,已经有客户在使用它。然后我们发现BaseWidget公司又发布了一个新版的BaseWidget。由于对其中的新功能抱有热切的期待,我们立即购买了它,并试图生成新版的MyWidget。可是,生成的时候失败了,原因在于BaseWidget添加了自己的DoWidgetThings方法。
{
public void DoWidgetThings()
{
// 忽略细节。
}
}
这是一个问题,我们的基类悄无声息地在其内引入了一个和子类同名的方法。有两种修正该问题的方法。首先,我们可以更改DoWidgetThings方法的名字:
{
public void DoMyWidgetThings( )
{
// 忽略细节。
}
}
或者,我们可以使用new修饰符:
public class MyWidget : BaseWidget
{
public new void DoWidgetThings( )
{
// 忽略细节。
}
}
当然,随着时间的推移,我们的用户可能也会试图去使用BaseWidget.DoWidget- Things()方法。这时候我们又回到了原来的问题上:两个方法看起来相同,但实际上不同。因此,我们应该考虑new修饰符所带来的长期不良影响。有时候,短期内更改方法名所导致的不方便可能仍然是值得的。
综上所述,使用new修饰符必须小心。如果不分青红皂白地使用,便会在对象上出现含混不清的方法调用。只有在“新版的基类增添的成员与子类中已存在的成员发生了冲突”这样特殊的情况下,我们才应考虑使用 new修饰符。即使在这种情况下,在使用它之前我们也要慎重考虑。除此之外,我们不应该再在任何其他情况下使用new修饰符。
出处:http://VisualStudio.cnblogs.com/
个人网站:H2站长论坛
本文版权归作者和博客园还有H2站长论坛共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。