Essential C# 5.0 Note - Chapter Six - Inheritance

Inheritance

public class PdaItem
{
public string Name { get; set; }

public DateTime LastUpdated { get; set; }
}

public class Contact : PdaItem
{
public string Address {get;set;}
public string Phone {get;set;}
}

public class Appointment : PdaItem
{
// ...
}

public class Customer : Contact
{
// ...
}

public class Program
{
public static void Main()
{

// Derived types can be implicitly converted to
// base types
Contact contact = new Contact();
PdaItem item = contact;

// ...

// Base types must be cast explicitly to derived types
contact = (Contact)item;

// ...
}
}

The derived type, Contact, is a PdaItem and can be assigned directly to a variable of type PdaItem.

This is known as an implicit conversion because no cast operator is required and the conversion will, on principle, always succeed; it will not throw an exception.

The reverse, however, is not true.

A PdaItem is not necessarily a Contact; it could be an Appointment or some other derived type.

Therefore, casting from the base type to the derived type requires an explicit cast, which at runtime could fail.

To perform an explicit cast, identify the target type within parentheses prior to the original reference.

An implicit conversion to a base class does not instantiate a new instance.

Instead, the same instance is simply referred to as the base type and the capabilities (the accessible members) are those of the base type.

Similarly, casting down from the base class to the derived class simply begins referring to the type more specifically, expanding the available operations.

The restriction is that the actual instantiated type must be an instance of the targeted type (or something derived from it).

Defining Custom Conversions

class GPSCoordinates
{
// ...

public static implicit operator UTMCoordinates(
GPSCoordinates coordinates)

{

// ...
}
}

Private Access Modifier

Non-nested derived classes cannot access members declared as private in a base class.

Protected Access Modifier

Protected members in the base class are only accessible from the base class and other classes within the derivation chain.

public class Contact : PdaItem
{
void Save()
{

// Instantiate a FileStream using <ObjectKey>.dat
// for the filename.
FileStream stream = System.IO.File.OpenWrite(
ObjectKey + ".dat");
}

void Load(PdaItem pdaItem)
{

// ERROR: 'pdaItem.ObjectKey' is inaccessible
// due to its protection level
// pdaItem.ObjectKey = ...;


Contact contact = pdaItem as Contact;
if(contact != null)
{
contact.ObjectKey = ...;
}

// ...
}
}

A subtlety shown in the Contact.Load() method is worth noting. Developers are often surprised that from code within Contact it is not possible to access the protected ObjectKey of an explicit PdaItem, even though Contact derives from PdaItem. The reason is that a PdaItem could potentially be an Address, and Contact should not be able to access protected members of Address. Therefore, encapsulation prevents Contact from potentially modifying the ObjectKey of an Address. A successful cast to Contact will bypass the restriction as shown. The governing rule is that accessing a protected member from a derived class requires compile-time determination that the protected member is an instance of the derived class (or a class further derived from it).

Extension Methods

We cover interfaces and how to use them with extension methods in the next chapter.

Single Inheritance

C# is a single-inheritance programming language (as is the CIL language to which C# compiles).

For the rare cases that require a multiple-inheritance class structure, one solution is to use aggregation.
Instead of one class inheriting from another, one class contains an instance of the other.