|
|
|
C# has been designed from the ground up to use inheritance. As such, the normal behavior that occurs during inheritance is almost always the behavior you actually want. Still, you should be aware of two keywords, "new" and "sealed", which change the way that inheritance would normally work. Let's look at "sealed", first, since it's the least deviant (and easiest to explain) of the two. "sealed" simply prevents you from using inheritance. For example, consider the following two classes... class Animal { } sealed class SealedAnimal { } If we were to create a Dog class, it would be able to inherit from the Animal class. However, it would not be able to inherit from the SealedAnimal class, because of the "sealed" keyword. What "sealed" basically says is, "nobody can inherit this, or redefine it... it is what it is, no matter what!" As a result, no class has the ability to inherit from SealedAnimal. Remember, too, that the "sealed" keyword isn't limited to base classes. You could make the Dog class, which inherits from Animal, sealed, so that other classes could inherit from Animal, but not Dog. This is particularly useful if it will be difficult or impossible for a programmer to write an inheriting class that works in harmony with the rest of your program, or the class that it would inherit from. Don't give people the option to inherit stuff that they can't handle! You can also apply the "sealed" keyword to methods, to prevent people from overriding them. However, you may only use the sealed keyword on an actual override method. Why? Because if the method is virtual, the "sealed" keyword is contradicting the "virtual" keyword, and if the method is neither virtual nor override, no one will be trying to override it, anyway! Let's say that Animal has a virtual Speak() method. If Dog inherits from Animal, and you override the Speak() method like this... class Dog: Animal { public sealed override void Speak() { } } ...you'll still be able to write your override in the Dog class. However, because Speak() is sealed in the Dog class, a class that inherits from Dog will not be able to write its own override for Speak(). Once again, this is a useful feature when there is an important reason to allow overriding up to a certain point in the inheritance tree, but forbid it beyond that point. Perhaps the code in the Dog's Speak() method is integral to any class that inherits from it, so that you don't want to permit changes to it. As with the "abstract" keyword, "sealed" is about enforcing ideas in your program's structure that would otherwise only be implied. Now I'll explain "new". You've already seen the "new" keyword used when creating instances of a class, and this is its primary purpose. However, the "new" keyword can also be applied to methods, in order to break the rules of inheritance entirely. Let's look back to the Speak() method in Dog. Say that the Speak() method in Dog actually serves a completely different function than the Speak() method in Animal. To completely disassociate the Dog's Speak() method with its counterpart in Animal, we declare it as "new", instead of "override".... public new void Speak() { } What effect does this actually have on our program? Well, consider our familiar DoAnimalActions() method: static void DoAnimalActions(Animal animal) { animal.Speak(); } If we pass a Dog object into this method, the Dog's version of Speak() will not be called. What happens is this: the program sees the Speak() method call, remembers that the animal object is actually of type Dog(), and goes to the Dog class to see if there's an override in there. It will see the Speak() method in dog, but it will also see that it isn't an override... it's a "new" version of Speak(), and has nothing to do with the Speak() method in Animal. So, the program will ignore it, and fall back on using the Speak() method in Animal. In effect, the Speak() method in Animal and the Speak() method in Dog are hidden from each other. The key difference between "override" and "new" is in use. "override" means that the method has different code than the original one in the base class, but it can (and should) be used for the same purpose. "new" completely disassociates the inheriting class's method from the original one. It should not be used for the same purpose as the original one, and is therefore ignored in the DoAnimalActions() method. "new" can be applied to any method in an inheriting class that has the same name and signature as a method in the base class, even if it wasn't virtual (indeed, since this keyword effectively "breaks" inheritance, we couldn't care less whether the original method was virtual or not). The only exception to this rule is abstract methods. Because abstract methods must be overridden in an inheriting class, it is not possible for an inheriting class to declare its version of the method to be "new". Having said all that, the ability to declare methods as "new" is actually one feature of C# that I'm not very fond of. When an inherited class and a base class both have a method of the same name, the logical assumption is that it serves the same purpose (but perhaps uses different code). If the inherited method serves a different purpose, then it ought to have a different name, to avoid confusion. And of course, once you've changed the inherited method's name, you don't need to apply the "new" keyword to it, anyway. "new" methods are misleading, because they imply a virtual/override relationship where none exists. If the Run() method in Animal makes the animal dash, but the Run() method in Dog makes the animal run for a political office (same word, completely different purpose), then they shouldn't have the same name. On the other hand, if you name the methods Run() and RunForOffice(), then the programmer can immediately tell that they do different things. And you won't have to "break" the rules of inheritance, either. That's just my two cents on the matter. At any rate, here is your code sample, outlining the effects and usage of "sealed" and "new": using System; namespace C_BreakRules { class Class1 { static readonly string ln = Environment.NewLine; [STAThread] static void Main(string[] args) { // Both calls will run Animal's version of Run(), because // the "new" keyword hides Dog's version of Run() from Animal CallRun(new Animal()); CallRun(new Dog()); Dog dog = new Dog(); dog.Run(); PromptForExit(); } static void CallRun(Animal animal) { animal.Run(); } static void PromptForExit() { Console.Write(ln + "Program complete! Hit enter to exit..."); Console.ReadLine(); } } class Animal { public virtual void Eat() { } public void Run() { Console.WriteLine("The animal dashes across the ground."); } } class Dog: Animal { // Example of a method that can't be overridden // Terrier and Bulldog classes would not be able // to write overrides for Eat(), if they inherit // from the Dog class. public sealed override void Eat() { } // We can use "new" to say that the Dog's Run() method // has nothing to do with Animal's Run() method. Avoid // "breaking" inheritence this way whenever possible public new void Run() { Console.WriteLine("The dog attempts to get elected."); } } // Example of a class that can't be inherited sealed class SealedAnimal { } } |
|