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:
object c = MakeObject( );
// Call through MyClass reference:
MyClass cl = c as MyClass;
cl.MagicMethod( );
// Call through MyOtherClass reference:
MyOtherClass cl2 = c as MyOtherClass;
cl2.MagicMethod( );
When the new modifier is involved, that just isn't the case:
public class MyClass
{
public void MagicMethod( )
{
// details elided.
}
}
public class MyOtherClass : MyClass
{
// Redefine MagicMethod for this class.
public new void MagicMethod( )
{
// details elided
}
}
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.
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.
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 thoseand only thosevirtual. 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.
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:
public class MyWidget : BaseWidget
{
public void DoWidgetThings( )
{
// details elided.
}
}
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:
public class BaseWidget
{
public void DoWidgetThings()
{
// details elided.
}
}
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:
public class MyWidget : BaseWidget
{
public void DoMyWidgetThings( )
{
// details elided.
}
}
Or, you could use the new modifier:
public class MyWidget : BaseWidget
{
public new void DoWidgetThings( )
{
// details elided.
}
}
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.
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.
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.