Effective C#之Item 29:Use the new Modifier Only When Base Class Updates Mandate It

  rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Item 29: Use the new Modifier Only When Base Class Updates Mandate It

仅仅当新的基类需要时才考虑使用new修饰符

You use the new modifier on a class member to redefine a nonvirtual member inherited from a base class. Just because you can do something doesn't mean you should, though. Redefining nonvirtual methods creates ambiguous behavior. Most developers would look at these two blocks of code and immediately assume that they did exactly the same thing, if the two classes were related by inheritance:

在类成员上使用new修饰符来重定义从基类继承的非虚成员。你能做,但是并不意味着你应该做。重新定义非虚方法,会产生模糊的行为。如果2个类之间有继承的关系,多数开发者会看下这2块代码,迅速的假设它们做完全相同的事情。

 

  1.    object c = MakeObject();
  2.  
  3.     // Call through MyClass reference:
  4.     MyClass cl = c as MyClass;
  5.     cl.MagicMethod();
  6.  
  7.     // Call through MyOtherClass reference:
  8.     MyOtherClass cl2 = c as MyOtherClass;
  9.     cl2.MagicMethod();
  10.  

When the new modifier is involved, that just isn't the case:

当涉及到new修饰符时,情况就不同了:

 

  1.    public class MyClass
  2.     {
  3.         public void MagicMethod()
  4.         {
  5.             // details elided.
  6.         }
  7.     }
  8.  
  9.     public class MyOtherClass : MyClass
  10.     {
  11.         // Redefine MagicMethod for this class.
  12.         public new void MagicMethod()
  13.         {
  14.             // details elided
  15.         }
  16. }

This kind of practice leads to much developer confusion. If you call the same function on the same object, you expect the same code to execute. The fact that changing the reference, the label, that you use to call the function changes the behavior feels very wrong. It's inconsistent. A MyOtherClass object behaves differently in response to how you refer to it. The new modifier does not make a nonvirtual method into a virtual method after the fact. Instead, it lets you add a different method in your class's naming scope.

这种做法会让多数开发者犯迷糊。如果你在同样的对象上调用同样的方法,那么就是希望执行相同的代码。事实上,改变了引用(你用来调用方法的标签)就改变了行为,这是很不对的,是矛盾的。你如何引用MyOtherClass对象,会产生不同的行为。在犯下错误之后,new修饰符没有使得一个非虚方法变成虚方法,相反,它让你在你的类的命名范围内增加了一个不同的方法。

Nonvirtual methods are statically bound. Any source code anywhere that references MyClass.MagicMethod() calls exactly that function. Nothing in the runtime looks for a different version defined in any derived classes. Virtual functions, on the other hand, are dynamically bound. The runtime invokes the proper function based on the runtime type of the object.

非虚方法是静态绑定的。任何地方的任何调用MyClass.MagicMethod()的代码都精确的调用该方法。在运行时,不会寻找在任何派生类里面定义的不同版本。从另一方面说,虚方法是动态绑定的,运行时根据对象的运行时类型调用恰当的方法。

The recommendation to avoid using the new modifier to redefine nonvirtual functions should not be interpreted as a recommendation to make everything virtual when you define base classes. A library designer makes a contract when making a function virtual. You indicate that any derived class is expected to change the implementation of virtual functions. The set of virtual functions defines all behaviors that derived classes are expected to change. The "virtual by default" design says that derived classes can modify all the behavior of your class. It really says that you didn't think through all the ramifications of which behaviors derived classes might want to modify. Instead, spend the time to think through what methods and properties are intended as polymorphic. Make those and only those virtual. Don't think of it as restricting the users of your class. Instead, think of it as providing guidance for the entry points you provided for customizing the behavior of your types.

该建议“仅仅当新的基类需要时才考虑使用new修饰符”不应该被解释为:当你定义基类的时候将所有的东西都定义为虚的。库的设计者在将一个方法定义为虚的时候,就做出了一个约定:暗示所有的派生类都被期望着来改变虚方法的实现。虚方法的集合定义了所有派生类将可以改变的行为。“默认为虚”的设计表明派生类可以修改你的类的所有行为。这意味着我们没有仔细思考派生类到底会更改哪些部分的行为。相反,应该花一些时间来考虑应该将哪些方法和属性声明为多态。我们应该仅仅将它们设置为虚。不要认为它是对你的类的用户的限制。相反,将这种做法当作是在为定制类型行为提供一些入口点

There is one time, and one time only, when you want to use the new modifier. You add new to incorporate a new version of a base class that contains a method name that you already use. You've already got code that depends on the name of the method in your class. You might already have other assemblies in the field that use this method. You've created the following class in your library, using BaseWidget that is defined in another library:

有且仅有一种情况,你希望使用new修饰符。当你使用了基类的新版本时,而它又包含了一个你已经使用的方法名字,为了不与其发生冲突,需要添加new修饰符。你已经有了依赖于类的这个方法名的代码,可能已经有了其它的使用该方法的程序集。你已经在库里面创建了下面的类,它使用了在另一个库里面定义的BaseWidget

  1.     public class MyWidget : BaseWidget
  2.     {
  3.         public void DoWidgetThings()
  4.         {
  5.             // details elided.
  6.         }
  7.    }

You finish your widget, and customers are using it. Then you find that the BaseWidget company has released a new version. Eagerly awaiting new features, you immediately purchase it and try to build your MyWidget class. It fails because the BaseWidget folks have added their own DoWidgetThings method:

你已经完成了widget,客户正在使用它。然后,你发现BaseWidget公司已经发布了一个新版本。由于很渴望里面的新特性,你迅速的购买了并试图建立自己的MyWidget类。因为BaseWidget的人们已经添加了他们自己的DoWidgetThings方法,你的努力失败了。

  1.     public class BaseWidget
  2.     {
  3.         public void DoWidgetThings()
  4.         {
  5.             // details elided.
  6.         }
  7. }

This is a problem. Your base class snuck a method underneath your class's naming scope. There are two ways to fix this. You could change that name of your DoWidgetThings method:

这是一个问题。你的基类在你的类的名称范围内偷偷的定义了一个方法。有2种方法来修正。你可以修改你的DoWidgetThings方法的名字。

  1.     public class MyWidget : BaseWidget
  2.     {
  3.         public void DoMyWidgetThings()
  4.         {
  5.             // details elided.
  6.         }
  7. }

Or, you could use the new modifier:

或者,使用new修饰符

  1.     public class MyWidget : BaseWidget
  2.     {
  3.         public new void DoWidgetThings()
  4.         {
  5.             // details elided.
  6.         }
  7. }

If you have access to the source for all clients of the MyWidget class, you should change the method name because it's easier in the long run. However, if you have released your MyWidget class to the world, that would force all your users to make numerous changes. That's where the new modifier comes in handy. Your clients will continue to use your DoWidgetThings() method without changing. None of them would be calling BaseWidget.DoWidgetThings() because it did not exist. The new modifier handles the case in which an upgrade to a base class now collides with a member that you previously declared in your class.

如果你有MyWidget类的所有客户代码的访问权,你应该修改方法名字,因为从长期上来讲这比较容易。然而,如果你已经向全世界发布了MyWidget类,那将会迫使你的所有用户做出巨大的修改。这就是new修饰符发威的时候了。你的客户将继续使用你的DoWidgetThings()方法,不需要做任何修改。没有人会调用BaseWidget.DoWidgetThings(),因为它不存在。new修饰符处理这种情况:新的基类增添的成员与在你的类中先前已经声明的成员发生了冲突。

Of course, over time, your users might begin wanting to use the Base Widget.DoWidgetThings() method. Then you are back to the original problem: two methods that look the same but are different. Think through all the long-term ramifications of the new modifier. Sometimes, the short-term inconvenience of changing your method is still better.

当然,随着时间的流逝,你的用户可能希望使用BaseWidget.DoWidgetThings()方法,那么你又陷入了原来的问题中:2个方法看起来一样,但是本质不同。因此,应该考虑new修饰符带来的长期不良影响。有时,短期内更改方法名所导致的不方便可能仍然是值得的。

The new modifier must be used with caution. If you apply it indiscriminately, you create ambiguous method calls in your objects. It's for the special case in which upgrades in your base class cause collisions in your class. Even in that situation, think carefully before using it. Most importantly, don't use it in any other situations.

new修饰符必须小心使用。如果不加区分的使用,会在你的对象的方法调用上产生混乱。它是用于特定情况的:对基类的更新会在你的类里面产生冲突。甚至在那种情况下,在使用前也要仔细考虑。最重要的是,不要在其他任何情况下使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值