Interfaces

An interface is a contract with the type that inherits and implements the interface. As part of that contract, interfaces are immutable. If the interface changes, it could break the type that inherited the interface. Conversely, the type promises to implement the interface fully.

For example, all vehicles have the same basic behavior: ignition on, ignition off, turn left, turn right, accelerate, and decelerate. A car, truck, bus, and motorcycle are all considered vehicles. As such, they must implement the basic functions of any vehicle. The vehicle interface defines that baseline. The different vehicle types inherit the vehicle interface and implement the behavior specific to that type. A car accelerates differently from a motorcycle. A motorcycle turns differently than a bus does. An interface mandates a set of behaviors, but not the implementation. The derived type is free to implement the interface in an appropriate manner. In this way, interface members are similar to abstract members of a class.

You cannot create an instance of an interface. Interfaces must be implemented.

Any class or structure that inherits from an interface commits to implementing the members of that interface. An interface is a set of related functions that must be implemented in a derived type. Members of an interface are implicitly both public and abstract.

Interfaces are similar to abstract classes. First, both types must be inherited. Second, you cannot create an instance of either. Finally, both have members that must be implemented in the derived type. Although abstract classes and interfaces are similar, there are several differences:

  • An abstract class can contain some implementation. Interfaces have no implementation.

  • Abstract classes can inherit from other classes and interfaces. Interfaces can inherit only other interfaces.

  • Abstract classes can contain fields. Interfaces do not have states.

  • Abstract classes have constructors and destructors. Interfaces have neither.

  • Interfaces can be inherited by structures. Abstract classes are not inheritable by structures.

When choosing between defining an interface and defining an abstract class with all abstract members, select the interface. With an interface, the derived type still can inherit from other types. Furthermore, the interface is more straightforward. The ZClass in the following code is simply not as clear as a comparable interface:

public abstract class ZClass {
    abstract public void MethodA (int a);
    abstract public void MethodB (int a);
    abstract public void MethodC (int a);
    abstract public void MethodD (int a);
}

public interface IZ {
    void MethodA (int a);
    void MethodB (int a);
    void MethodC (int a);
    void MethodD (int a);
}

This is the syntax of an interface:

attributes accessibility modifiers interface identifier:baselist  { body }

An interface can begin with an attribute. Use attributes to describe an interface further, such as the Obsolete attribute. For accessibility, non-nested interfaces can be public or internal. Nested interfaces have all the accessibility options. Interfaces can be nested in classes and structs, but not in other interfaces. For nested interfaces, the new modifier is also applicable. In this syntax, baselist is a list of zero or more interfaces from which the derived interface inherits. The interface body contains the members of the interface, which consists of methods, properties, indexers, and events. As mentioned previously, interface members are implicitly both public and abstract.

When interfaces inherit from other interfaces, the inherited members are added to the derived interface. The inherited interface essentially extends the current interface. A type inheriting from the derived interface must implement the aggregate interface, which includes the current interface and members that were inherited from other interfaces. If a member appears in both the derived interface and base interfaces, there is no ambiguity. However, a compiler warning is generated. Add the new modifier to the duplicated member in the derived interface to avoid the warning.

In the following code, IZ and IY are interfaces. The IY interface inherits from IZ. MethodB is a member of both interfaces. For that reason, IY.MethodB hides IZ.MethodB with the new modifier. Types implementing the IY interface must implement the MethodA, MethodB, and MethodC methods, which are the combined members of both interfaces:

public interface IZ {
    void MethodB();
    void MethodC();
}

public interface IY : IZ {
    void MethodA();
    new void MethodB();
}

A type can inherit multiple interfaces. The type must implement the members of each interface that is inherited. Those interfaces may have identical members. A single implementation in the derived type satisfies the requirement from all interfaces providing the ambiguous member. Therefore, the same method inherited from multiple interfaces is not ambiguous. In the following code, the ZClass inherits both the IX and IY interfaces. MethodA appears in both interfaces. However, it is necessary to implement the method only once in the derived type. The ZClass type implements MethodA and MethodB functions, which are the combined members from both interfaces:

interface IX {
    void MethodA();
}

interface IY{
    void MethodA();
    void MethodB();
}

public class ZClass : IX, IY{
    public void MethodA() {
    }

    public void MethodB() {
    }
}

Implementing Interfaces

Classes or structs that inherit from an interface should define each member of the interface in the type. The function signature in the type must match the function header in the interface. Members of an interface are implicitly public. The implementation in the type also must be public.

In the following code, the Car class inherits the IVehicle interface. As required, the members of the IVehicle interface are implemented in the Car class:

public interface IVehicle {
    void IgnitionOn();
    void IgnitionOff();
    void TurnLeft();
    void TurnRight();
}

public class Car : IVehicle{

    public void IgnitionOn() {
    }

    public void IgnitionOff() {
    }

    public virtual void TurnLeft() {
    }

    public virtual void TurnRight() {
    }
}

A class that inherits multiple interfaces has multiple specialties. An amphibious vehicle is a combination of a vehicle and a boat. In the following code, the Amphibious class inherits both the IVehicle and IBoat interfaces. The members of both interfaces are implemented in the Amphibious class. As discussed, duplicate interface members are implemented only once:

using System;

namespace Donis.CSharpBook {

    public interface IVehicle {
       void IgnitionOn();
       void IgnitionOff();
       void TurnLeft();
       void TurnRight();
    }

    public interface IBoat {
       void IgnitionOn();
       void IgnitionOff();
       void TurnLeft();
       void TurnRight();
       void FishFinder();
       void Rudder();
    }

    public class Amphibious : IVehicle, IBoat {
        public void IgnitionOn() {
        }
        public void IgnitionOff() {
        }
        public void TurnLeft() {
        }
        public void TurnRight() {
        }
        public void FishFinder() {
        }
        public void Rudder() {
        }
    }
}

Explicit Interface Member Implementation

You can implement and bind a member to a specific interface. This is called explicit interface member implementation. In the type, prefix the member name with the interface name. Members implemented in this manner are not accessible through the derived type. For this reason, the accessibility modifier is omitted from explicitly implemented interface members. When used, explicitly interface members are available only via an interface cast.

In the following code, the ZClass implements the IA interface. IA.MethodA is implemented explicitly, whereas IA.MethodB is implemented in a normal manner. In ZClass, MethodA is prefixed with the interface name (IA.MethodA). This binds MethodA specifically to interface IA. MethodB is visible from the ZClass interface but MethodA is hidden. You must cast an instance of ZClass to the IA interface to call MethodA. This is shown in the code:

using System;

public class Starter {
    public static void Main() {
        ZClass obj = new ZClass();
        obj.MethodA(); // Error
        obj.MethodB();
        IA i = obj;
        i.MethodA();
    }
}

public interface IA {
   void MethodA();
   void MethodB();
}

public class ZClass : IA {

    void IA.MethodA() {
        Console.WriteLine("IA.MethodA");
    }

    public void MethodB() {
        Console.WriteLine("IA.MethodB");
    }
}

Explicit interface implementation is useful when needing to implement ambiguous members inherited from more than one interface separately. Normally, they are consolidated into a single implementation. This is also helpful when the interface and the derived type share the same member. The interface member is implemented explicitly to preserve the separate implementation in the derived type. Explicit interface implementation provides separate implementation without ambiguity. To call the interface implementation, cast to the appropriate interface first.

The following code is an updated version of the amphibious vehicle. Steering a boat is different from steering a car. Therefore, TurnLeft and TurnRight are explicitly implemented for the IBoat interface. TurnLeft and TurnRight also are implemented explicitly for the IVehicle interface. The TurnLeft and TurnRight functions of the Amphibious type (the derived type) delegate to IVehicle.TurnLeft and IVehicle.TurnRight, making them the default behaviors:

using System;

namespace Donis.CSharpBook {

    public class Starter {
        public static void Main() {

            Amphibious marinevehicle = new
                    Amphibious();
            marinevehicle.IgnitionOn();
            marinevehicle.TurnLeft();
            IBoat boatmaneuvers = marinevehicle;
            boatmaneuvers.TurnLeft();
            marinevehicle.IgnitionOff();
        }
    }

    public interface IVehicle {
       void IgnitionOn();
       void IgnitionOff();
       void TurnLeft();
       void TurnRight();
    }

    public interface IBoat {
       void IgnitionOn();
       void IgnitionOff();
       void TurnLeft();
       void TurnRight();
       void FishFinder();
       void Rudder();
    }

    public class Amphibious : IVehicle, IBoat {

       public void IgnitionOn() {
           Console.WriteLine("Ignition on.");
       }

       public void IgnitionOff() {
           Console.WriteLine("Ignition off.");
       }

       public void TurnLeft() {
           IVehicle vehicle = this;
           vehicle.TurnLeft();
       }

       public void TurnRight() {
           IVehicle vehicle = this;
           vehicle.TurnRight();
       }

       void IVehicle.TurnLeft() {
           Console.WriteLine("Turn vehicle left.");
       }

       void IVehicle.TurnRight() {
           Console.WriteLine("Turn vehicle right.");
       }

       void IBoat.TurnLeft() {
           Console.WriteLine("Turn boat left.");
       }

       void IBoat.TurnRight() {
           Console.WriteLine("Turn boat right.");
       }

       public void FishFinder() {
           Console.WriteLine("Fish finder in use.");
       }

       public void Rudder() {
           Console.WriteLine("Adjust rudder.");       }
    }
}

Another reason to use explicit interface implementation is to hide some portion of a class or struct from specific clients. You can use an interface cast to expose as much or as little of the component as desired. In a class library, a developer can expose different aspects of an internal component by returning an interface or using an interface parameter in a callback.

In the following code, the Car class inherits and implements the IVehicle and IEngine interfaces. The two interfaces have no overlapping members. A driver doesn’t usually interface directly with the engine. That is usually left to the mechanic. Therefore, the IEngine interface is hidden in the Car class:

using System;

namespace Donis.CSharpBook {

     public class Starter {
        public static void Main() {
            Car auto = new Car();
            auto.IgnitionOn();
            auto.IgnitionOff();

            //  Access engine.

            IEngine e = auto.AccessEngine();

            //  Inspect engine.
        }
    }

    public interface IVehicle {
        void IgnitionOn();
        void IgnitionOff();
        void TurnLeft();
        void TurnRight();
    }

    public interface IEngine {
        void Alternator();
        void Ignition();
        void Transmission();
    }

    public class Car : IVehicle, IEngine {

        public void IgnitionOn() {
            Console.WriteLine("Ignition on.");
            AccessEngine().Ignition();
        }

        public void IgnitionOff() {
            Console.WriteLine("Ignition off.");
        }

        public void TurnLeft() {
            Console.WriteLine("Turn left.");
        }

        public void TurnRight() {
            Console.WriteLine("Turn right.");
        }

        public IEngine AccessEngine() {
            return this;
        }

        void IEngine.Alternator() {
            Console.WriteLine("Alternator.");
        }

        void IEngine.Ignition() {
            Console.WriteLine("Ignition");
        }

        void IEngine.Transmission() {
            Console.WriteLine("Transmission");
        }
    }
}

Reimplementation of Interfaces

When a class inherits from an interface, it must implement the members of the interface. In the derived class, the implemented functions can be virtual. Descendants of the derived class will inherit the implemented functions. However, descendants of the derived class where the interface is implemented cannot be cast to the interface. Only types that directly implement an interface can be cast to that interface. However, descendants of that class can cast back to the interface through the parent type. This is demonstrated in the following code:

interface IY {
    void MethodA();
    void MethodB();
}

public class ZClass : IY {
    void IY.MethodA() {
        Console.WriteLine("IY.MethodA");
    }

    public virtual void MethodA() {
    }

    public virtual void MethodB() {
    }
}

public class WClass : ZClass {

    public override void MethodA() {
        Console.WriteLine("WClass.MethodA");
    }

    public override void MethodB() {
        Console.WriteLine("WClass.MethodB");
    }
}

class Startup {
    public static void Main() {
        IY obj = (IY)((ZClass)new WClass());
        obj.MethodA();  // Writes IY.MethodA
        obj.MethodB();  // Writes WClass.MethodB
    }
}

Explicitly implemented interface members cannot be overridden in descendants. If you want to replace the behavior of an inherited explicitly implemented member, reimplement the explicit interface member in the descendant. Inherit the interface in the descendant of the derived type, where the interface is originally inherited, and explicitly reimplement only the interface members that are being hidden. This is demonstrated in the following code:

interface IY {
    void MethodA();
    void MethodB();
}

public class ZClass : IY {
    void IY.MethodA() {
        Console.WriteLine("test");
    }

    public virtual void MethodA() {
    }

    public virtual void MethodB() {
    }
}

public class WClass : ZClass, IY {
    void IY.MethodA() {
        Console.WriteLine("IY.MethodA in WClass");
    }

    public override void MethodB() {
        Console.WriteLine("WClass.MethodB");
    }
}

class Startup {
    public static void Main() {
        IY obj = (IY)((ZClass) new WClass());
        obj.MethodA();  // Writes IY.MethodA in WClass
        obj.MethodB();  // Writes WClass.MethodB
    }
}

This statement appears in the preceding code: IY obj = (IY)((ZClass) new WClass()). This statement casts the derived instance to a base type, where the IY interface is implemented explicitly. The result is then successfully cast to the IY interface.

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

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