Inheritance

A C# class can inherit from another class to extend or customize that class. A class can only inherit from a single class but can be inherited by many classes, thus forming a class hierarchy. At the root of any class hierarchy is the object class, which all objects implicitly inherit from. Inheriting from a class requires specifying the class to inherit from in the class declaration, using the C++ colon notation:

class Location { // Implicitly inherits from object
  string name;

  // The constructor that initializes Location
  public Location(string name) {
    this.name = name;
  }
  public string Name {get {return name;}}
  public void Display( ) {
    Console.WriteLine(Name);
  }
}
class URL : Location { // Inherit from Location
  public void Navigate( ) {
    Console.WriteLine("Navigating to "+Name);
  }
  // The constructor for URL, which calls Location's constructor
  public URL(string name) : base(name) {}
}

URL has all the members of Location, and a new member, Navigate:

class Test {
  static void Main( ) {
    URL u = new URL("http://microsoft.com");
    u.Display( );
    u.Navigate( );
  }
}

Tip

The specialized class and general class are referred to as either the derived class and base class or the subclass and superclass .

Class Conversions

A class D may be implicitly upcast to the class B it derives from, and a class B may be explicitly downcast to a class D that derives from it. For instance:

URL u = new URL( );
Location l = u; // upcast
u = (URL)l; // downcast

If the downcast fails, an InvalidCastException is thrown.

as operator

The as operator allows a downcast to be made that evaluates to null if the downcast fails:

u = l as URL;

is operator

The is operator can test if an object is or derives from a specified class (or implements an interface). It is often used to perform a test before a downcast:

if (l is URL)
  ((URL)l).Navigate( );

Polymorphism

Polymorphism is the ability to perform the same operation on many types, as long as each type shares a common subset of characteristics. C# custom types exhibit polymorphism by inheriting classes and implementing interfaces (see the Section 2.10).

In the following example, the Show method can perform the operation Display on both a URL and a LocalFile, because both types inherit the characteristics of Location:

class LocalFile : Location {
  public void Execute( ) {
    Console.WriteLine("Executing "+Name);
  }
  // The constructor for LocalFile, which calls URL's constructor
  public LocalFile(string name) : base(name) {}
}
class Test {
  static void Main( ) {
    URL u = new URL( );
    LocalFile l = new LocalFile( );
    Show(u);
    Show(l);
  }
  public static void Show(Location loc) {
    Console.Write("Location is: ");
    loc.Display( );
  }
}

Virtual Function Members

A key aspect of polymorphism is that each type can implement a shared characteristic in its own way. One way to permit such flexibility is for a base class to declare function members as virtual. Derived classes can provide their own implementations for any function members marked virtual in the base class (see Section 2.10):

class Location {
  public virtual void Display( ) {
    Console.WriteLine(Name);
    }
    ...
}
class URL : Location {
  // chop off the http:// at the start
  public override void Display( ) {
    Console.WriteLine(Name.Substring(6));
  }
  ...
}

URL now has a custom way of displaying itself. The Show method of the Test class in the previous section will now call the new implementation of Display. The signatures of the overridden method and the virtual method must be identical, but unlike Java and C++, the override keyword is also required.

Abstract Classes and Members

A class can be declared abstract. An abstract class may have abstract members, which are function members without implementation that are implicitly virtual. In earlier examples, we specified a Navigate method for the URL type and an Execute method for the LocalFile type. You can, instead, declare Location an abstract class with an abstract method called Launch:

abstract class Location {
  public abstract void Launch( );
}
class URL : Location {
  public override void Launch( ) {
    Console.WriteLine("Run Internet Explorer...");
  }
}
class LocalFile : Location {
  public override void Launch( ) {
    Console.WriteLine("Run Win32 Program...");
  }
}

A derived class must override all its inherited abstract members or must itself be declared abstract. An abstract class can’t be instantiated. For instance, if LocalFile doesn’t override Launch, LocalFile itself must be declared abstract, perhaps to allow Shortcut and PhysicalFile to derive from it.

Sealed Classes

A class can prevent other classes from inheriting from it by specifying the sealed modifier in the class declaration:

sealed class Math {
  ...
}

The most common scenario for sealing a class is when that class comprises only static members, such as is the case with the Math class of the base class library. Another effect of sealing a class is that it enables the compiler to turn all virtual method invocations made on that class into faster nonvirtual method invocations.

Hiding Inherited Members

Aside from its use for calling a constructor, the new keyword can also hide the data members, function members, and type members of a base class. Overriding a virtual method with the new keyword hides, rather than overrides, the base class implementation of the method:

class B {
  public virtual void Foo( ) {}
}
class D : B {
  public override void Foo( ) {}
}
class N : D {
  public new void Foo( ) {} // hides D's Foo
}
N n = new N( );
n.Foo( ); // calls N's Foo
((D)n).Foo( ); // calls D's Foo
((B)n).Foo( ); // calls D's Foo

A method declaration with the same signature as its base class must explicitly state whether it overrides or hides the inherited member.

Versioning Virtual Function Members

In C#, a method is compiled with a flag that is true if the method overrides a virtual method. This flag is important for versioning. Suppose that you write a class that derives from a base class in the .NET Framework and then deploy your application to a client computer. The client later upgrades the .NET Framework, and the .NET base class now contains a virtual method that happens to match the signature of one of your methods in the derived class:

class B { // written by the library people
  virtual void Foo( ) {...} // added in latest update
}
class D : B { // written by you
  void Foo( ) {...}
}

In most object-oriented languages, such as Java, methods are not compiled with this flag, so a derived class’s method with the same signature is assumed to override the base class’s virtual method. This means a virtual call is made to type D’s Foo method, even though D’s Foo is unlikely to have been implemented according to the specification intended by the author of type B. This can easily break your application. In C#, the flag for D’s Foo will be false, so the runtime knows to treat D’s Foo as new, which ensures that your application will function as it was originally intended. When you get the chance to recompile with the latest framework, you can add the new modifier to Foo, or perhaps rename Foo to something else.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.96.155