Inheritance, Interfaces, and Abstract Classes - C# Fundamentals - Sams Teach Yourself C# 5.0 in 24 Hours (2013)

Sams Teach Yourself C# 5.0 in 24 Hours (2013)

Part I: C# Fundamentals

Hour 5. Inheritance, Interfaces, and Abstract Classes


What You’ll Learn in This Hour

Inheritance and polymorphism

Abstract classes and members

Working with interfaces


In the last hour, you learned how classes in C# provide the language support required for object-oriented and component-oriented programming through encapsulation and abstraction. Although these aspects of object-oriented programming are important, they don’t provide a good mechanism for expressing a hierarchical relationship between specialized variations of a class.

Inheritance provides a natural way to express such relationships. Through inheritance, you can create a completely new class that inherits the characteristics and behaviors from its parents. Polymorphism, which is the capability of a type to be used like another type, is a natural result of inheritance.

In this hour, you learn how C# provides support for inheritance and polymorphism through the use of abstract classes and interfaces. You also learn how C# enables you to prevent a class from being extended.

Inheritance and Polymorphism

Just as children inherit characteristics and behaviors from their parents, classes can inherit characteristics and behavior as well. Inheritance, also called derivation, in object-oriented programming enables a new class to be created (called a child or derived class) that inherits the characteristics and behaviors from its parents (called base classes).

Inheritance enables you to reduce the apparent complexity of a problem into manageable parts. These parts form conceptual layers that provide increasing amounts of specialization or generalization, depending on your point of view, describing in a natural way the hierarchical nature of certain problems expressed using an “is-a” relationship.


Caution: Multiple Inheritance

The general idea of inheritance is simple. However, many object-oriented programming languages enable derived classes to inherit from multiple parents, called multiple inheritance.

Multiple inheritance does not change the requirement that the classes have an “is-a” relationship and can be a powerful mechanism, but that power can also result in considerable complexity in the implementation. It can also cause ambiguity when trying to understand the derivation chain of a class because a class with two parents enables two different inheritance paths to a particular base class.

To remove the possible ambiguity and because the number of scenarios for which multiple inheritance is the only appropriate solution is rather small, C# only allows single inheritance.


Typically, specialization is when the new class has additional data or behavior that is not part of the inherited (base) class. Specialization can also occur when the base class specifies that only an action or behavior exists but does not implement that behavior. It is then the responsibility of the derived classes to provide the implementation.

By creating a new type derived from an existing type, you inherit characteristics and behaviors from the parent type. Inheritance also enables derived classes to make several changes from their base class. The derived class can do the following:

• Add new private data.

• Add new behavior.

• Redefine existing behavior.

Again, taking the car example from the last hour, Figure 5.1 shows a possible inheritance chain for a car. A car is a four-wheel vehicle, which in turn is a vehicle. The vehicle is the base, or root, class and provides behavior and data common for all vehicles. As we go down the hierarchy, we find the four-wheel and two-wheel vehicle base classes, which are specializations of vehicle that add additional behavior and data. Finally, we find the car, truck, and motorcycle classes, which are derived classes that might also add additional behavior and data.

Image

Figure 5.1. A class diagram.

In object-oriented programming, polymorphism is the capability of one type to be used like another type. Typically, there are two ways this is achieved:

• One type inherits (or derives) from another type, enabling it access to the same actions and public data as its parent.

• Both types implement a compatible public interface, enabling the same actions and public data but possibly different implementations.

Looking at the class diagram in Figure 5.1, it should be clear that although a car and a truck are clearly not the same type, either of those could be substituted for a four-wheel vehicle because they both inherit from that class. Likewise, although a motorcycle could not be substituted for a four-wheel vehicle, it could, along with a car or a truck, be substituted for a vehicle.

As you can see, polymorphism relies heavily on the ideas presented in encapsulation, abstraction, and inheritance. Without these aspects, it would be virtually impossible for one class to be substituted for another.


Note: Polymorphism

Polymorphism is one of those words that might sound like it is complicated, but it really isn’t. Polymorphism is a natural and common occurrence. The word is the combination of the Greek words poly (meaning many) and morphe (meaning shape or form), literally meaning many shapes or forms.


Inheritance is easily accomplished in C# by providing the name of the class being inherited on the class declaration. Listing 5.1 shows the hierarchy described by Figure 5.1 in code.

Listing 5.1. A Class Hierarchy in Code


public class Vehicle { }

public class FourWheeledVehicle : Vehicle { }

public class TwoWheeledVehicle : Vehicle { }

public class Car : FourWheeledVehicle { }

public class Truck : FourWheeledVehicle { }

public class Motorcycle : TwoWheeledVehicle { }


This type of inheritance is also called implementation inheritance because you are actually inheriting an implementation from the parent class. Now that you have a code representation of the hierarchy, how would you go about using it?


Caution: Designing Class Hierarchies

The one thing that inheritance does not enable is removing data or behaviors. If you find that you need to remove behavior or data from one of your derived classes, it is most likely because your class hierarchy is not designed correctly.

Designing class hierarchies is not always an easy task and usually takes several attempts to get it right. The best approach is to spend a little more time thinking about the relationships between objects you know and those that might be needed later. If your class hierarchy is overly shallow (not a lot of inheritance) or overly deep (a lot of inheritance), you might want to rethink the relationships.

Remember, not everything in a class hierarchy must be related to one another. It’s perfectly acceptable to have a hierarchy that is conceptually made up of several smaller ones.


In C#, an expression assigned to a variable must be known to be compatible with the type of that variable. This means the following code is not allowed:

Car c = new Car();
Truck t = c;

This should make sense, as a truck is not the same thing as a car. However, logically both a truck and a car are a FourWheeledVehicle and, in turn, a Vehicle so the following code is allowed:

Car c = new Car();
Truck t = new Truck();
Vehicle v1 = c;
Vehicle v2 = t;


Note: Upcasting and Downcasting

The process of casting a derived class to one of its base classes is called upcasting. Casting from a base class to one of its derived classes is called downcasting.


Even though c and v1 both refer to the same Car object, there is an important distinction. When you access c, you are doing so through a variable declared to be of type Car, allowing you access to the members defined by Car and by its base class FourWheeledVehicle, and subsequently Vehicle. However, when you access v1, you are doing so through a variable declared to be of type Vehicle that allows you access only to those members defined by Vehicle.

Think of it this way: A car knows that it is a vehicle and therefore enables access to anything a vehicle can do, but a vehicle knows nothing of a car and can therefore allow you access only to what it knows.

Although you can move upward in the class hierarchy (going from a more derived class to a less derived one), you cannot move downward. For example, the following is not allowed:

Vehicle v1 = new Vehicle();
Car c = v1;

This might be surprising at first until you remember that a Vehicle could represent any one of five different types. As a result, it is not possible to implicitly assign an expression of a more general type to a variable of a more specific one.

To achieve this, you must explicitly tell the compiler that you want to downcast the base class to the derived class. In this case, you would want to write:

Vehicle v1 = new Vehicle();
Car c = (Car)v1;

Although this code is legal, it does pose a problem. What would happen if you wrote the following instead?

Vehicle v1 = new Vehicle();
Vehicle v2 = new Truck();
Car c = (Car)v2;

This code is still legal and will compile without error, but will result in an InvalidCastException at runtime, saying that you cannot cast an object of type Truck to type Car.


Go To

HOUR 11, “HANDLING ERRORS USING EXCEPTIONS,” for more information on exceptions.


The way around this problem is to follow the “trust but verify” philosophy. This simply means that you trust that the code will compile and run, but you verify that the variable of the base class type is actually the correct derived type before performing the cast. Listing 5.2 shows a variety of ways you can accomplish “trust but verify.”

Listing 5.2. “Trust but Verify” Code


Car c = new Car();
Truck t = new Truck();
Vehicle v1 = c;
Vehicle v2 = t;

if (typeof(Car).IsAssignableFrom(v1.GetType()))
{
c = (Car)v1;
Console.WriteLine(c.GetType());
}

if (v1 is Car)
{
c = (Car)v1;
Console.WriteLine(c.GetType());
}

c = v1 as Car;
if (c != null)
{
Console.WriteLine(c.GetType());
}


The first method uses the underlying type system in C# to determine if the type Car (the result of the typeof(Car) call) is assignable from the type of v1 (the result of the v1.GetType() call), and, if so, explicitly casts v1 to Car. A base class is always assignable from one of its derived classes.

The second method is somewhat simpler and asks the type system if v1 is a Car, and if so, explicitly casts v1 to Car.

The third method is the simplest option, and says if v1 is convertible to Car then perform the conversion and return the result; otherwise, return null.


Try It Yourself: Simple Class Inheritance and Polymorphism

To implement the class hierarchy shown in Listing 5.1 and explore how inheritance and polymorphism behave, follow these steps. Keep Visual Studio open at the end of this exercise because you will use this application later.

1. Create a new console application.

2. Add a new class file named Vehicles.cs and implement the basic class hierarchy shown in Listing 5.1.

3. In the Main method of the Program.cs file, enter the following code:

Vehicle v1 = new Vehicle();
Car c1 = (Car)v1;

4. Run the application by pressing Ctrl+F5. You should encounter an InvalidCastException, as shown in Figure 5.2.

Image

Figure 5.2. Result showing an InvalidCastException.

5. Remove the statements you previously entered from step 2, and replace it with the code shown in Listing 5.2.

6. Run the application again by pressing Ctrl+F5 and observe that the output matches what is shown in Figure 5.3.

Image

Figure 5.3. Result of working with class inheritance and polymorphism.


Listing 5.3 shows a modified version of the code hierarchy from Listing 5.1, providing constructors for some of the derived classes that call one of the base class constructors.


Caution: Constructor Chaining and Default Constructors

If you don’t explicitly chain a base class constructor, the compiler tries to chain the default constructor.

The problem here is that not all classes have a public default constructor, in which case forgetting to explicitly chain the correct base class constructor can result in a compiler error.


Listing 5.3. Constructors in Derived Classes


public class Vehicle
{
private Vehicle() { }

public Vehicle(int wheels)
{
Console.WriteLine("The number of wheels requested is {0}", wheels);
}
}

public class FourWheeledVehicle : Vehicle
{
public FourWheeledVehicle() : base(4) { }
}

public class TwoWheeledVehicle : Vehicle
{
public TwoWheeledVehicle() : base(2) { }
}

public class Car : FourWheeledVehicle { }

public class Truck : FourWheeledVehicle { }

public class Motorcycle : TwoWheeledVehicle { }



Try It Yourself: Constructor Chaining

By following these steps, you modify the class hierarchy you created in the previous section to explore constructor chaining. If you closed Visual Studio, repeat the previous exercise first. Be sure to keep Visual Studio open at the end of this exercise because you will use this application later.

1. Open the file named Vehicles.cs.

2. Modify the classes to reflect the code shown in Listing 5.3.

3. In the Main method of the Program.cs file, replace the code with code to create a new car, motorcycle, truck, and three-wheeled vehicle.

4. Run the application using Ctrl+F5 and observe that the output matches what is shown in Figure 5.4.

Image

Figure 5.4. Result of working with constructor chaining.


Working with Inherited Members

Sometimes a derived class needs to have a method or property with the same name but a different behavior, accomplished using member overriding.

To keep things as clear as possible, C# requires the use of two different keywords to override a class member. In the base class, the member declaration must contain the virtual keyword, whereas in the derived class, the member declaration must contain the override keyword.

Typically, virtual members are simple in the behavior they implement—if they provide a behavior at all. Their purpose is to ensure that in all cases, the derived classes will have the member available and that it will perform some nominal default behavior. The expectation is that the derived classes will override the virtual member with a more appropriate and specific behavior.

Just as you can with constructors, you can use the base keyword to access the original implementation. The base keyword behaves similarly to the this keyword, except it refers to the immediate base class rather than the current class.

Member overriding has certain restrictions:

• The overriding member declaration cannot change the accessibility declared by the virtual member.

• Neither a virtual nor overridden member can be declared as private.

• Both declarations must have the same signature.

• The overridden member cannot contain the new, static, or virtual modifiers.


Caution: Default Virtual Members

Whereas some object-oriented languages, such as Java, default to making members virtual, C# does not. This means that you must be explicit about the possibility of a member being overridden by including the virtual keyword because only members declared as virtual can be overridden.



Try It Yourself: Overriding Base Class Members

Follow these steps to modify the classes you previously created to create virtual and overridden class members. If you closed Visual Studio, repeat the previous exercise first. Be sure to keep Visual Studio open at the end of this exercise because you will use this application later.

1. Open the file named Vehicles.cs.

2. Modify the Vehicle class to include a virtual Operate method that simply prints the word Default to the console.

3. Modify the FourWheeledVehicle and TwoWheeledVehicle classes to override the Operate method, and print “Driving a four-wheeled vehicle” and “Riding a two-wheeled vehicle,” respectively, after the call to base.Operate().

4. Modify the Car class to also override the Operate method, but this time replace the call to base.Operate() with a Console.WriteLine statement that prints “Driving a car.”

5. In the Main method of the Program.cs file, add a call to Operate after each instance created. Follow that with a Console.WriteLine() to print an empty line.

6. Run the application using Ctrl+F5 and observe that the output matches what is shown in Figure 5.5.

Image

Figure 5.5. Result showing how overriding base class members behave.


As you saw from the previous exercise, an overridden member is implicitly virtual and can be further overridden by subsequent derived classes. You did this when you implemented the override for Drive in the Car class, even though FourWheeledVehicle already overrode Drive. Normally this behavior is desirable; however, there might be times when you want to prevent it from occurring.


Caution: Member Hiding

In addition to member overriding, C# also allows you to hide a base class member. This is typically done when you need to redefine a nonvirtual base class member and is achieved by adding the new keyword in front of the new definition. Because member hiding is based on the signature, you can use member hiding to change the return type of the member as well.

Hiding base class members is something that can produce unexpected, or at least unintended, results. Although hiding base class members can sometimes be intentional in the derived class, it is often a result of a change to the base class (which you might or might not have control of) that causes you to unintentionally hide a base class member or an incorrectly designed class hierarchy.

As a result, when a base class member is hidden, the compiler generates a warning to let you know. If you are sure that is what you want to do, you should use the new keyword on the derived member. The use of the new keyword does not remove the need to be cautious when hiding base class members; it simply makes it explicit.


You can prevent further inheritance for a class member or a class by sealing it. To seal a class member, you include the sealed keyword in addition to the override keyword. To seal a class, you simply include the sealed keyword.

For example, to seal the FourWheeledVehicle.Operate method from the “Overriding Base Class Members” Try It Yourself, you would add the sealed keyword to the method signature, as shown in the following:

public class FourWheeledVehicle : Vehicle
{
public FourWheeledVehicle() : base(4) { }

public sealed override void Operate()
{
base.Operate();
Console.WriteLine("Driving a four-wheeled vehicle");
}
}

Similarly, if you wanted to prevent further derivation from the TwoWheeledVehicle class, you would add the sealed keyword to the class definition, as shown here:

public sealed class TwoWheeledVehicle : Vehicle
{
public TwoWheeledVehicle() : base(2) { }

public override void Operate()
{
base.Operate();
Console.WriteLine("Riding a two-wheeled vehicle");
}
}


Note: Sealing Classes

Designing classes properly for inheritance can be a lot of work, so you have three choices:

• Leave the class unsealed but do the work to make it safely inheritable, which might end up being unnecessary if no one ever inherits from your class.

• Leave the class unsealed but don’t do the work. This puts the burden on the consumer for understanding how your class can be safely extended.

• Seal the class. If you are reasonably certain no one will need to inherit from your class, it is probably the best option. It is always possible to unseal the class later, which should have no breaking impact on code that is already using it. Sealing a class also allows certain additional runtime optimizations to occur during JIT compilation.


Abstract Classes and Members

Although inheriting from a class provides a lot of benefit, there are times when you need to provide derived classes with a standard implementation and need to guarantee that a derived class provides an implementation for a particular property or method, or it simply makes sense to have a class that can have no instances. If you think back to the vehicle hierarchy, there are simply no objects that are “vehicles” that are not also some more specific kind of vehicle. To achieve these goals, C# provides the abstract modifier that can be applied to both classes and class members.

By declaring a class as abstract, you prevent it from being instantiated directly. As a result, abstract classes typically have protected constructors rather than public ones. If you don’t provide a default constructor, the compiler creates a protected default constructor for you. An abstract class can contain virtual, nonvirtual, and abstract members. An abstract member is declared with the abstract modifier but does not provide an implementation. Listing 5.4 shows an example of the Vehicle class as an abstract class containing an abstract method named Operate.


Tip: Static Classes

The compiler actually implements static classes as sealed abstract classes, preventing them from being instantiated or inherited.


Listing 5.4. The Abstract Vehicle Class


public abstract class Vehicle
{
int wheels;

protected Vehicle() { }

public Vehicle(int wheels)
{
this.wheels = wheels;
}

public abstract void Operate();
}


Unlike virtual members, which a derived class can optionally override, abstract members must be overridden in a concrete (nonabstract) derived class. If the derived class is also abstract, it does not need to override a base class abstract member. Because an abstract member has no implementation until overridden in a derived class, it is not possible for that overridden member to call the same member from the base class.


Try It Yourself: Using Abstract Classes

By following these steps, you explore how to create an abstract class, and use it as a base class for derived classes. If you closed Visual Studio, repeat the previous exercise first. Be sure to keep Visual Studio open at the end of this exercise because you will use this application later.

1. Open the file named Vehicles.cs.

2. Modify the Vehicle class so that it is an abstract class and make the Operate method an abstract method.

3. Correct the resulting compiler errors.

4. Run the application using Ctrl+F5 and observe that the output matches what is shown in Figure 5.6.

Image

Figure 5.6. Result of working with abstract classes.


Working with Interfaces

Because C# does not enable you to inherit from multiple base classes, you need to carefully choose that base class. Fortunately, C# provides an alternative that does enable multiple inheritance. An interface defines a set of common characteristics and behaviors, in the form of public properties and methods, that all derived classes are guaranteed to implement. Think of an interface as a partial type definition, or a type description.

Like abstract classes, an interface cannot be directly instantiated and can have methods, properties, and events. Interfaces cannot contain fields, constructors, or a destructor.


Caution: Interfaces Are Not Contracts

It is common to say that an interface defines a contract that any derived classes must implement. This is only true in the sense that the inherited interface defines the method signatures and properties guaranteed to be available on the derived class.

In no way does this provide any guarantee of a specific implementation. It is entirely possible to satisfy the interface while providing no useful implementation whatsoever. The interface simply requires any class inheriting it to have specific properties and methods.


You declare an interface in much the same way as declaring a class, substituting the keyword interface for the class keyword. An interface can be internal, public, protected, or private; however, if you don’t explicitly indicate the accessibility, interfaces default to internal accessibility. All interface members are automatically public and cannot specify an access modifier. Because interface members are also automatically abstract, they cannot provide an implementation.

When a class inherits an interface, called interface inheritance or interface implementation, it inherits only the member names and signatures because the interface doesn’t provide an implementation. This means the derived class must provide an implementation for all members defined by that interface. To inherit an interface, you provide the name of the interface just as if you were inheriting a class. You can inherit from multiple interfaces simply by providing a comma-separated list of interface names.


Note: Inheriting Both a Base Class and Interfaces

C# uses a positional notation to indicate the base class and the interfaces in the inheritance list. If a class inherits from both a base class and one or more interfaces, the base class is always listed first.


You can also “mix and match” inheritance, meaning that you can inherit from only a base class, inherit only one or more interfaces, or inherit from both a base class and one or more interfaces. If a base class inherits from an interface, any classes that derive from that base class inherit that implementation.

Although interfaces cannot inherit from a class, they can inherit from other interfaces. Interface inheritance simply means that to satisfy one interface, you must also satisfy the other inherited interfaces. This enables you to create highly specialized interfaces and then aggregate those interfaces together to form a larger interface. This is particularly useful if you have multiple unrelated classes that need to implement similar functionality, such as saving data to a compressed ZIP file. The behavior and characteristics related to this could be defined by interfaces, which could then be included as part of other interfaces that define your business objects.


Tip: Interfaces and Extension Methods

In the context of interfaces, extension methods become even more powerful as you can also extend interfaces. Doing this allows all types that implement that interface to gain the extension method. In fact, the entire Language Integrated Query (LINQ) functionality for the generic collections is implemented this way.


When an interface inherits from another interface, the derived interface defines only members that are new; it does not redefine the members of the inherited interfaces. When a class implements this aggregated interface, it must provide implementations for members defined in all the interfaces.

Where the real power and flexibility of using interfaces shine is when used in combination with abstract classes. Because interface methods are implicitly abstract, an abstract class does not need to provide an implementation for an interface member. Instead, it can provide its own abstract member for the interface member. In that case, any derived classes must override it and provide an implementation.

Looking at the vehicle hierarchy from Listing 5.1, what would it take to introduce the concept of emergency vehicles, such as fire trucks, police cars, and police motorcycles? You could introduce a new class, called EmergencyVehicle, but it isn’t entirely clear where this new class fits in the existing hierarchy because it would need to be a base class at the same level as FourWheeledVehicle and TwoWheeledVehicle, and any concrete classes would want to inherit from EmergencyVehicle and Truck, Car, or Motorcycle. You already know this type of multiple inheritance is not possible, but how would this change if you used interfaces instead?


Try It Yourself: Working with Interfaces

These steps explore how to use both interfaces and base classes to create a flexible class hierarchy. If you closed Visual Studio, repeat the previous exercise first.

1. Create a new interface named IVehicle that defines a method named Operate and modify the Vehicle class to inherit from it. The IVehicle.Operate method should have the same signature as the Vehicle.Operate method.

2. Create a new interface named IEmergencyVehicle that inherits from IVehicle and provides a void method named SoundSiren that takes no parameters.

3. Create three new classes named PoliceCar, PoliceMotorcycle, and FireTruck that derive from the appropriate base class and implement the IEmergencyVehicle interface. For the implementation of SoundSiren, simply print a message to the console.

4. In the Main method of the Program.cs file, replace the existing code with code that will:

a. Create a new instance of a Car, assigned to a variable named car, and call the Operate method.

b. Assign a new instance of PoliceCar to car and call the Operate method.

c. Create a new instance of PoliceCar named policeCar and assign car to it. You need to cast car to be of type PoliceCar.

d. Call SoundSiren on policeCar.

5. Run the application using Ctrl+F5, and observe that the output matches what is shown in Figure 5.7.

Image

Figure 5.7. Result of working with interfaces.


When you implemented the interfaces from the last exercise, you used implicit interface implementation, in which the class simply declares public members that match those defined by the interface. This is the most common form of interface implementation.

What happens if the class implements multiple interfaces that specify a member with the same signature, as shown in Listing 5.5?

Listing 5.5. Multiple Interface Inheritance


interface IVehicle
{
void Operate();
}

interface IEquipment
{
void Operate();
}

class PoliceCar : IVehicle, IEquipment
{
public void Operate()
{
}
}


In this case, the compiler makes the public method match both interface members. If this isn’t what you intended, you must use explicit interface implementation, as shown in Listing 5.6, which requires that you fully qualify the name of the member being implemented. In explicit interface implementation, you do not provide an access modifier because the implementing member is implicitly public but only explicitly through the interface.

Listing 5.6. Multiple Explicit Interface Inheritance


interface IVehicle
{
void Operate();
}

interface IEquipment
{
void Operate();
}

class PoliceCar : IVehicle, IEquipment
{
void IVehicle.Operate()
{
}

void IEquipment.Operate()
{
}
}


This means that the implementing class must be converted, either implicitly or explicitly, to that interface to have access to the member. As a result, explicit interface implementation has the effect of hiding members from casual use.

Summary

You have now completed your understanding of how C# classes provide a language implementation for object-oriented programming. You learned how class inheritance enables polymorphic objects, chaining base class constructors, and how to override or hide inherited class members. You also learned about abstract classes, and learned how to prevent a class or class member from further inheritance by sealing. Finally, you learned about interfaces and interface implementation.

Q&A

Q. Does C# support multiple inheritance?

A. No, C# supports only single inheritance, enabling a class to inherit from only a single parent, or base, class.

Q. What is polymorphism?

A. Polymorphism is the ability of one type to be used like another type.

Q. What is upcasting and downcasting?

A. The process of casting a derived class to one of its base classes is called upcasting. Casting from a base class to one of its derived classes is called downcasting.

Q. What is member hiding?

A. Member hiding enables a derived class to have a member with the same name as a base class member with exactly the same behavior.

Q. What is a virtual member?

A. A virtual member contains the virtual modifier in the base class and might or might not provide default behavior. Derived classes can override a virtual member and provide more appropriate specific behavior.

Q. Is it possible to prevent a class from being inherited?

A. Yes, classes marked with the sealed modifier cannot be inherited.

Q. What is an interface?

A. An interface defines a set of common characteristics and behaviors that all derived classes are guaranteed to implement but cannot contain any implementation, and all interface members must be public. Interfaces do not guarantee an implementation, only that the inheriting class must also contain members matching the signature of the interface members.

Q. Does C# support multiple interface inheritance?

A. Yes, C# supports inheriting from multiple interfaces. Interfaces can also inherit from other interfaces.

Q. Can extension methods be used to extend interfaces?

A. Yes, extension methods can be used to extend interfaces in exactly the same way they are used to extend classes.

Workshop

Quiz

1. Why is class inheritance also called implementation inheritance?

2. What is the correct way to hide an inherited member?

3. What are the restrictions on member overriding?

Answers

1. Class inheritance is also called implementation inheritance because the derived class inherits the implementation of the base class.

2. To hide an inherited member, you should use the new keyword on the derived member to explicitly indicate that the base class member is being hidden.

3. Member overriding has the following restrictions:

• The overriding member declaration cannot change the accessibility declared by the virtual member.

• Neither a virtual nor overridden member can be declared as private.

• Both declarations must have the same signature.

• The overridden member cannot contain the new, static, or virtual modifiers.

Exercises

1. Add a public constructor to the Photo class in the PhotoViewer project that accepts a string parameter named path. This constructor should contain an empty body and call the Photo(Uri) constructor you previously added. Next, add an override for the ToString method that returns the result of calling source.ToString().

2. Add a public interface named IPhoto that mimics the current public class members of the Photo class. Change the Photo class to implement this new interface.