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

来源:互联网 发布:服务器状态监测软件 编辑:程序博客网 时间:2024/06/08 13:26
 

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

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

You usethe new modifier on a class member to redefine a nonvirtual member inheritedfrom a base class. Just because you can do something doesn't mean you should, though.Redefining nonvirtual methods creates ambiguous behavior. Most developers wouldlook at these two blocks of code and immediately assume that they did exactlythe 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 thenew 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. }

Thiskind of practice leads to much developer confusion. If you call the samefunction on the same object, you expect the same code to execute. The fact thatchanging the reference, the label, that you use to call the function changesthe behavior feels very wrong. It's inconsistent. A MyOtherClass object behavesdifferently in response to how you refer to it. The new modifier does not makea nonvirtual method into a virtual method after the fact. Instead, it lets youadd a different method in your class's naming scope.

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

Nonvirtualmethods are statically bound. Any source code anywhere that referencesMyClass.MagicMethod() calls exactly that function. Nothing in the runtime looksfor a different version defined in any derived classes. Virtual functions, onthe other hand, are dynamically bound. The runtime invokes the proper functionbased on the runtime type of the object.

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

Therecommendation to avoid using the new modifier to redefine nonvirtual functionsshould not be interpreted as a recommendation to make everything virtual whenyou define base classes. A library designer makes a contract when making afunction virtual. You indicate that any derived class is expected to change theimplementation of virtual functions. The set of virtual functions defines allbehaviors that derived classes are expected to change. The "virtual bydefault" design says that derived classes can modify all the behavior ofyour class. It really says that you didn't think through all the ramificationsof which behaviors derived classes might want to modify. Instead, spend thetime 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 usersof your class. Instead, think of it as providing guidance for the entry pointsyou provided for customizing the behavior of your types.

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

There isone time, and one time only, when you want to use the new modifier. You add newto incorporate a new version of a base class that contains a method name thatyou already use. You've already got code that depends on the name of the methodin your class. You might already have other assemblies in the field that usethis method. You've created the following class in your library, usingBaseWidget 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.    }

Youfinish your widget, and customers are using it. Then you find that theBaseWidget company has released a new version. Eagerly awaiting new features,you immediately purchase it and try to build your MyWidget class. It failsbecause 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 isa 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 yourDoWidgetThings method:

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

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

Or, youcould use the new modifier:

或者,使用new修饰符

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

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

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

Ofcourse, 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 thesame but are different. Think through all the long-term ramifications of thenew modifier. Sometimes, the short-term inconvenience of changing your methodis still better.

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

The newmodifier must be used with caution. If you apply it indiscriminately, youcreate ambiguous method calls in your objects. It's for the special case inwhich upgrades in your base class cause collisions in your class. Even in thatsituation, think carefully before using it. Most importantly, don't use it inany other situations.

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