Deep C# - Interface |
Written by Mike James | ||||
Tuesday, 12 April 2022 | ||||
Page 2 of 3
Multiple InheritanceOne big difference, and the important difference, between class and Interface inheritance is that interfaces support multiple inheritance. You can make a class implement two interface specifications using: public class MyClass:IMyInterface1,IMyInterface2 This looks like multiple inheritance. However, as nothing but the method definitions are inherited, it isn’t nearly so dangerous or, of course, as useful. Some other facts worth knowing about interfaces are that a struct can inherit an Interface and an Interface can inherit from other interfaces. Of course, an Interface that inherits from another set of interfaces doesn’t have to implement anything and it simply serves to extend or add to the base interfaces. When an interface is inherited you have to implement all of the methods defined in all of the inherited interfaces. So why, if it is “dangerous” to inherit multiple classes, is it safer to inherit multiple interfaces? The problem with inheriting from more than one class is that you run the risk of inheriting a method and its implementation more than once, the so-called “diamond problem”. To be exact, the problem is whether there are multiple inherited implementations or a method which is used when the method is called on the derived class. Different languages solve the problem in different ways, but essentially you can either insist that the user implements any multiply defined methods or you need some rule that selects which method is used. C# solves the problem by not allowing multiple class inheritance, only multiple interface inheritance. In this case all that is inherited is the definition so you have to implement it and implementing it means that there is no conflict. This form of interface inheritance is the same as having class multiple inheritance, but insists that any method clash is resolved by implementing the method. This account is only correct if interfaces don’t provide their own implementation and in modern C# they can. This is a topic we need to return to, but first we need to look at how interfaces are used as types. Interfaces As TypeA class determines what methods an object has. An interface determines what methods an object has. Class determines a type hierarchy, so why shouldn’t we use interfaces in the same way? The answer is that we can. If you create a variable using an interface as a type then it can be used to reference any object that inherits the interface. However, you can only access the methods defined in the interface. For example: public interface IMyInterface { int myMethod(); } class MyClassA:IMyInterface { public int myMethod() { return 42; } public void myMethod1() { Console.WriteLine("implemented A"); } You can see that IMyInterface is just a single method, myMethod, which is implemented by class MyClassA. In the main program, notice that myObject is declared to be of type IMyInterface and is made to reference an object of type MyClassA. You can use myObject to call myMethod, but that is all as it is the only thing defined in the interface. This looks a lot like an upcast and that’s exactly what it is, with IMyInterface being regarded as a base type for MyClassA. Our type hierarchy dogma is now extended to include interfaces as well as classes. The principle that any derived class can be used in place of a base class now includes any class that is derived from an interface can be used as that interface type. Of course, the big difference is that you can’t instantiate an interface only inherit it, but the idea and the motivation is the same, type safety. You may be wondering what advantage there is in using an interface as a type. After all, the class that inherits the interface has all of the methods it defines. In other words if myMethod is defined by IMyInterface you can use: IMyInterface myObject = new MyClassA(); Console.WriteLine(myObject.myMethod()); or MyClassA myObject = new MyClassA(); Console.WriteLine(myObject.myMethod()); Where there is something to be gained is when you are defining input parameters. If you define a function as: void myFunction(IMyInterface a){}; then the parameter a can be any class that inherits from and implements IMyInterface and this “short circuits” the inheritance hierarchy. Notice that you cannot use any of the methods of the class beyond those of the interface and in this sense exactly what the class is that is passed to the function is irrelevant. Also notice that interfaces play by the same rules as the class hierarchy. That is, if an interface inherits another then a variable of the base type can reference an object that implements the derived type, for example: public interface IMyInterfaceA { int myMethodA(); } public interface IMyInterfaceB:IMyInterfaceA { int myMethodB(); } A class that implements IMyInterfaceB has to implement both methods: class MyClassA:IMyInterfaceB { public int myMethodA() { throw new NotImplementedException(); } public int myMethodB() { throw new NotImplementedException(); } } If you use a variable of type MyClassA then you can use both methods. If you use a variable of type MyInterfaceB then you can use both methods. However, if you use: IMyInterfaceA myObject = new MyClassA(); i.e. a variable of type MyInterfaceA then everything works but you can only use myMethodA. The interface hierarchy works in the same way as the class hierarchy. |
||||
Last Updated ( Tuesday, 12 April 2022 ) |