Interfaces in C#

0
11
What an interface is since the word interface has a different meaning based on the context it is used in. The word interface is often used to describe the public parts of a class, i.e. the part that can be seen from others classes. The graphical user interface (GUI) is another use of the word interface. Here we will describe the C# interface construct.
 
An Abstract class without any implementation just looks like an Interface; however there are lot of differences than similarities between an Abstract class and an Interface. Let’s explain both concepts and compare their similarities and differences.
 
Defining an Interface
 
An interface is a structure similar to an abstract class but with no implementation code. i.e., an interface is only composed of method declarations, indexers, events and constants. Here we will only work with method declarations. An interface can be used anywhere in the class design as instance variables or parameters in method signatures.
 
An interface is a contract defining that any class that implements an interface must implement all the method definitions given in the interface. An interface says what a user of the interface must do, but not how to do it.
 
An interface defines behavioral characteristics and places those behaviors in classes independent of the class hierarchy. When a class makes a contract by applying an interface, the class is said to implement the interface or inheriting from the interface.
 
So, what are interfaces good for if they don’t implement functionality? They are great for putting together plug-n-play like architectures where components can be interchanged at will.
 
Since all interchangeable components implement the same interface, they can be used without any extra programming. The interface forces each component to expose specific public members that will be used in a certain way.
 
Because interfaces must be defined by inheriting classes and structs, they define a contract. For instance, if class interfaceTest inherits from the IDisposable interface, it is making a statement that it guarantees it has the Dispose() method, which is the only member of the IDisposable interface.
 
Any code that wishes to use class interfaceTest may check to see if class interfaceTest inherits IDisposable. When the answer is true, then the code knows that it can call interfaceTest.Dispose(). Here you can see, how to define an interface:
 
MyInterface.cs
 
interface IMyInterface
{
    void MethodToImplement();
} 
 
The above example we define an interface named IMyInterface. It is common to use the prefix ‘I’ in front of an interface name, but it is not necessary. It makes it easier to read. This interface has a single method named MethodToImplement(). This could have been any type of method declaration with different parameters and return types.
 
I just choose to declare this method with no parameters and a void return type to make the example easy. Notice that this method does not have an implementation (instructions between curly braces – {}), but instead ends with a semi-colon, “;”. This is because the interface only specifies the signature of methods that an inheriting class or struct must implement.
 
Example of Implementing an Interface
 
When we implementing any interface, no implementation code is present there, but only method signatures is present. An implementing class must write the body code for the two methods:
 
  public interface IStudent
  {
    string GetName();
    void SetName(string name); 
  }
 
Here we are creating IStudent interface. It is common to use the prefix ‘I’ in front of an interface name, but it is not necessary. It makes it easier to read and understand the design illustrated whether it is a UML class diagram or a bunch of source files that you got from a third party.
 
And the service code implementing the interface is:
 
  public class AStudent: IStudent
  {
    string name;
  
    private string GetName()
    {
      return name;
    }

    private void SetName(string name)
    {
      this.name = name; 
    }
  }
 
Note that this method implementation has the exact same signature, parameters and method name, as defined in the IStudent interface. Any difference will cause a compiler error. Interfaces may also inherit other interfaces.
 
Multiple Interface Inheritance
 
A class only inherits from one class in C#, but can implement (inherit) as many interfaces as needed.
 
InterfaceInheritance.cs
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.IO;
namespace ConsoleApplication4
{
    interface IParentInterface
    {
        void ParentInterfaceMethod();
    }	

    //Interface IStudent inherits from IParentInterface
    interface IStudent : IParentInterface
    {
        string GetName(string name);
        void SetName();
    }

    //Class InterfaceInheritance inherits from IStudent
    class InterfaceInheritance : IStudent
    { 
        static void Main()
        {
            string st="IIII";
            InterfaceInheritance iImp = new InterfaceInheritance();
            iImp.GetName(st);
            iImp.ParentInterfaceMethod();
            iImp.SetName();
            Console.ReadLine();
        }

        public void SetName()
        {
            Console.WriteLine("SetName() method called.");
        }
        public string GetName(string name)
        {
            Console.WriteLine("GetName() method called.");
            return null;
        }

        public void ParentInterfaceMethod()
        {
            Console.WriteLine("ParentInterfaceMethod() called.");
        }
    }
}

 
The above code contains two interfaces: IStudent and the interface it inherits, IParentInterface. When one interface inherits another, any implementing class or struct must implement every interface member in the entire inheritance chain.
 
Since the InterfaceInheritance class in inherits from IStudent, it also inherits IParentInterface. Therefore, the InterfaceInheritance class must implement the GetName() method specified in the IStudent interface and the ParentInterfaceMethod() method specified in the IParentInterface interface.
 
Interface used in Polymorphism
 
Class and interface polymorphism is basically the same thing. The type (Room) of the variable (r1) and the type of the object (LivingRoom) stored in it is not exactly the same.
 
Room r1 = new LivingRoom()
 
The Room type is said to be of static type and the LivingRoom type is said to be of a dynamic type or subtype. The smart thing about using polymorphism is, among other factors that you can wait until runtime to decide what type to use for your variable.
 
Because of this, polymorphism is also called late binding. An interface does not have a constructor so one can only create an object of an interface as a subtype. Uses of interfaces as instance variables have to be as a subtype of the classes implementing the interface.
 
Diagram of the Interface Logic
 
Screenshot - InterfaceConstruct1.jpg
 
A class should not have too many responsibilities (some say three is a maximum). A Client class can delegate responsibilities or services to an interface. The service is provided by a ServiceProvider who is implementing the interface.
 
The Client class and the ServiceProvider class only communicate through the interface. The client and the service provider do not know about each other (indirection). Every service provider who is implementing the interface is accepted by the client, making the use of interfaces a flexible programming tool.
 
An Example Using Interfaces
 
In the given example below three different graphical user interfaces (here the word interface is used about the user interface and not the interface construct) are used to present the result of three different ways of reading a file. The client classes of the code sample are: BlueGUI, RedGUI and YellowGUI.
 
The three other classes providing a service reading different file types. The provided services are the classes: XMLInput (reading an XML file), INIInput (reading an INI/text file) and BinInput (reading a binary file). The three client classes and the three services providing classes communicate indirectly through an interface called IInput.
 
This gives the flexibility of nine possible combinations when building an application. The purpose of this article is not to show how to read XML, INI text files or binary files, but to show how to use an interface as a means of indirection between communicating sets of classes.
 
Screenshot - InterfaceConstruct2.jpg
 
Class diagram shows the relationship between the three client classes (colour GUIs) and three service providing classes (input readers). The three client classes are using an instance of the interface and the three service providers are implementing the interface.
 
Sub-typing the three implementing classes of the interface is used to indirectly attach the client and service provider, in a driver class. In the sample code checkboxes are used to select the two classes to couple from the two set of classes.
 
InterfaceDemo.cs
 
The interface code:
 
 //The interface IInput is responsible handling string information.
  public interface IInput
  {
    // Returns a string value from a service
      string NotifyOutput();
  }
 
The RedGUI class:
 
  // The RedGUI is a form class using the IInput 
  // interface through the object input.
  public class RedGUI : System.Windows.Forms.Form
  {
      IInput input;

      public RedGUI(IInput input)
      {
          this.input = input;
          this.BackColor = System.Drawing.Color.Red;
          this.Show();
      }

      public void GetInput()
      {
          this.Text = input.NotifyOutput();
      }
  }
 
The BlueGUI class:
 
  // The BlueGUI class is a parallel to the RedGUI class.
  public class BlueGUI : System.Windows.Forms.Form
  {
      IInput input;

      public BlueGUI(IInput input)
      {
          this.input = input;
          this.BackColor = System.Drawing.Color.Blue;
          this.Show();
      }
        
      public void GetInput()
      {
          this.Text = input.NotifyOutput();
      }
  }
 
The YellowGUI class:
 
  // The YellowGUI class is a parallel to the RedGUI class.
  public class YellowGUI : System.Windows.Forms.Form
  {
      IInput input;

      public YellowGUI (IInput input)
      {
          this.input = input;
          this.BackColor = System.Drawing.Color.Yellow;
          this.Show();
      }
        
      public void GetInput()
      {
          this.Text = input.NotifyOutput();
      }
  }
 
The XMLInput class:
 
// XMLInput reads some elements from an xml file.
  // XMLInput is a subtype of the interface IInput.
  public class XMLInput : IInput
  {
      // An empty constructor
      public XMLInput() {}

      // Implementation of interface  
      public string NotifyOutput()
      {
          return ReadXML();
      }

      // Simple read of an XML file
      public string ReadXML()
      {
          string str = "";
          try 
          {
              XmlDocument doc = new XmlDocument();
              string xmlFile = System.Windows.Forms.Application.StartupPath + 
                                                               "\\input.xml";
              if (System.IO.File.Exists(xmlFile))
              {                
                  doc.Load(@xmlFile);
                  XmlNodeList node1 = doc.GetElementsByTagName("node2");
                  foreach(XmlNode node2 in node1)
                  {
                      str += node2.ChildNodes[0].FirstChild.Value;
                  }
              }
              else 
              {
                  Console.WriteLine("Missing the input.xml from" + 
                                           " application directory.");
              }
          }
          catch (Exception e) 
          { 
              Console.WriteLine(e.Message);
          }
          return str;
      }
  }
 
The INIInput class:
 
  // INIInput reads some elements from a simple ini text file.
  // INIInput is a subtype of the interface IInput.
  public class INIInput : IInput
  {
      // An empty constructor
      public INIInput() {}

      // Implementation of interface
      public string NotifyOutput()
      {
          return ReadIni();
      }

      private string ReadIni()
      {
          string str = "";
          string iniFile = System.Windows.Forms.Application.StartupPath + "\\input.ini";
          try 
          {
              if (System.IO.File.Exists(iniFile))
              {
                  using (StreamReader sr = new StreamReader(iniFile))
                  {
                      String line;
                      while ((line = sr.ReadLine()) != null) 
                      {
                          str += line;
                      }
                  }
              }
              else 
              {
                Console.WriteLine("Missing the input.ini " + 
                                    "from application directory.");
              }
          }
          catch (Exception e) 
          {
              Console.WriteLine(e.Message);
          }
          return str;
      }
  }
 
The BinInput class:
 
// BinInput reads a string from a string object in a binary file.
  // BinInput is a subtype of the interface IInput.
  public class BinInput : IInput
  {
    public BinInput() { }
    
    // Implementation of interface
    public string NotifyOutput()
    {
      return ReadBin();
    }
    
    public string ReadBin()
    {
      AStringClass SomeStringClass = null;
      string binFile = System.Windows.Forms.Application.StartupPath + 
                                                        "\\input.bin";
      try 
      {
        if (File.Exists(binFile))
        {
          IFormatter formatter = new       
     System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
          Stream stream = new FileStream(binFile, FileMode.Open, 
                                    FileAccess.Read, FileShare.Read);
          SomeStringClass = (AStringClass)
formatter.Deserialize(stream);
          stream.Close();
        }
        else 
        {
          Console.WriteLine("Missing the input.bin" + 
                                      " from application directory.");
        }
      }
      catch (Exception e) 
      { 
        Console.WriteLine(e.Message); 
      }            
      return SomeStringClass.SomeString;
    }
  }
 
// We need this class to cast the serialized objects from the stream,
// when we read the input.bin file. Is also used to create the binary
// file.
// The create of the binary file is not used in this sample instead
// the input.bin file is coming with the source code.
 
  [Serializable]
  public class AStringClass
  {
    public string SomeString;
    
    public AStringClass() 
    {
      this.SomeString = "Text from a bin file";

      string binFile = 
System.Windows.Forms.Application.StartupPath + 
"\\input.bin";
      SaveToBinaryFile(binFile);
    }
    
    public void SaveToBinaryFile(string binFile)
    {
      try 
      {
        IFormatter formatter = new 
        System.Runtime.Serialization.
Formatters.Binary.BinaryFormatter();
        Stream stream = new FileStream(binFile,
 FileMode.OpenOrCreate, 
                                   
  FileAccess.Write, FileShare.None);
        formatter.Serialize(stream, this);
        stream.Close();        
      }
      catch (Exception e) 
      { 
           Console.WriteLine(e.Message); 
      }
    }
  }
 
The driver code is the master code using all the previous written classes and interface. Where all the classes are related to each other:
 
  …
  // Input XML file to Blue GUI
  IInput input = new XMLInput();
  BlueGUI blueGUI = new BlueGUI(input);
  blueGUI.GetInput();
    
  // Input XML file to Red GUI
  IInput input = new XMLInput();
  RedGUI redGUI = new RedGUI(input);
    
  // Input XML file to Yellow GUI
  IInput input = new XMLInput();
  YellowGUI yellowGUI = new YellowGUI(input);
  yellowGUI.GetInput();
            
  // Input INI file to Blue GUI
  IInput input = new INIInput();
  BlueGUI blueGUI = new BlueGUI(input);
  blueGUI.GetInput();
    
  // Input INI file to Red GUI
  IInput input = new INIInput();
  RedGUI redGUI = new RedGUI(input);
  redGUI.GetInput();
    
  // Input INI file to Yellow GUI
  IInput input = new INIInput();
  YellowGUI yellowGUI = new YellowGUI(input);
  yellowGUI.GetInput();            
                
  // Input Bin file to Blue GUI
  IInput input = new BinInput();
  BlueGUI blueGUI = new BlueGUI(input);
  blueGUI.GetInput();            
    
  // Input Bin file to Red GUI
  IInput input = new BinInput();
  RedGUI redGUI = new RedGUI(input);
  redGUI.GetInput();            
    
  // Input Bin file to Yellow GUI
  IInput input = new BinInput();
  YellowGUI yellowGUI = new YellowGUI(input);
  yellowGUI.GetInput();            
  …
 
In the Interface Demo application the driver code is controlled by radio buttons. The radio buttons are placed on top of the UML (Unified Modeling Language) illustration to explain the class relations.
 
Screenshot - InterfaceConstruct3.jpg
 
It’s easy to extend the application with new functionalities. If the customer wants a new functionality, say a new service provider reading from another source which is not currently present, the problem is solved by writing a new reader class implementing IInput.
 
This is a good way of extending the system since the developer does not have to change any existing code. Changing existing code could introduce new errors. A rule can be expressed as: “Once written don’t change.” Also, if the original developer is busy with other jobs, the job of implementing the new functionality can be given to his colleague.
 
The colleague who does not know the design and perhaps does not have the time to understand it, just writes a class capable of reading the new file format, so all that the new developer has to do extra is to implement the IInput interface. The original developer can then easily implement the new class in the system.
 
Casting with ‘is’ or ‘as’ Operator
 
System.Object obj = new System.Object();
System.Boolean result1 = (obj is System.Object); //if result 1 is true.
System.Boolean result 2 = (obj is Employee); //if result 2 is false.
 
If the object reference is null, the ‘is’ operator always returns false because there is no object available to check its type. The ‘is’ operator is typically used as follows:
 
if (obj is Employee) 
{
 	Employee e = (Employee) obj;
 	// Use e within the ‘if’ statement.
} 
 
In this code, the CLR is actually checking the object’s type twice: the ‘is’ operator first checks to see if obj is compatible with the Employee type. If it is, then inside the ‘if’ statement, the CLR again verifies that obj refers to an Employee when performing the cast. Because this programming paradigm is quite common, C# offers a way to simplify this code and improve its performance by providing an ‘as’ operator:
 
Employee e = o as Employee;
 	if (e != null) 
 	{
 	 	// Use e within the ‘if’ statement.
 	}
 
In this code, the CLR checks if obj is compatible with the Employee type, and if it is, ‘as’ returns a not null pointer to the same object. If obj is not compatible with the Employee type, then the ‘as’ operator returns null.
 
Notice that the ‘as’ operator causes the CLR to verify an object’s type just once. The ‘if’ statement simply checks whether or not e is null—this check can be performed much more efficiently than verifying an object’s type.
 
The ‘as’ operator works just like casting except the ‘as’ operator will never throw an exception. Instead, if the object can’t be cast, the result is null. You’ll want to check to see whether the resulting reference is null, or attempting to use the resulting reference will cause a System.NullReferenceException exception to be thrown.
 
The following code demonstrates:
 
System.Object obj = new System.Object(); // Creates a new Object object
Employee e = obj as Employee; // Casts obj to an Employee  
// The cast above fails: no exception is thrown, but e is set to null.
e.ToString(); // Accessing e throws a NullReferenceException.
 
Disadvantage of using Interface
 
One drawback of using interfaces is that it makes the code more complicated to understand.
 
Advantage of using Interface
 
The major advantage of using interfaces is that you add an extra dimension of communication to your design. Instead of only accessing classes through class inheritance, with interfaces you can make a backdoor, open for classes implementing a specific interface.
 
This construct makes the software flexible for future changes. The client using your class through a method argument or instance variable will not have to know anything about the class, but that it implements a specific interface.
 
Meaning: all classes implementing a given interface can be used. Uses of the interface construct decouple your design, resulting in low coupling and high cohesion. Low coupling means that when classes are decoupled they do not depend closely on each other. The chance of failures, faults or errors getting thrown when you make changes to existing code will be lower when the classes are decoupled. Often you do not have to change existing code.
 
All you do is add new code. Design of interfaces and their use in design patterns is at a more abstract level compared to the abstract level where your code is at. In fact, design patterns and the use of interface, abstracts the code itself. It means that if you have your design ready you can make the source code either in Java or C# directly from the design.
 
Practical Example (Abstract Class):
 
testAbstraction.cs
 
using System;
using System.Collections.Generic;
using System.Text;

namespace testAbstraction
{
    //Creating an Abstract Class
    abstract class abstractClass
    {
        //A Non abstract method
        public int AddTwoNumbers(int Num1, int Num2)
        {
            return Num1 + Num2;
        }

        //An abstract method, to be
        //overridden in derived class
        public abstract int MultiplyTwoNumbers(int Num1, int Num2);
    }

    class Program:abstractClass
    {
        static void Main(string[] args)
        {               
            //You can create an
            //instance of the derived class
            string a,b;
            Program calculate = new Program();
            Console.Write("Enter first number:  ");
            a = Console.ReadLine();
            Console.Write("Enter second number: ");
            b = Console.ReadLine();
            int added = calculate.AddTwoNumbers(int.Parse(a),int.Parse(b));
            int multiplied = calculate.MultiplyTwoNumbers(int.Parse(a),int.Parse(b));
            Console.WriteLine("\nAdded : {0}\nMultiplied : {1}", added, multiplied);
            Console.Read();
        }
        //using override keyword,
        //implementing the abstract method
        //MultiplyTwoNumbers
        public override int MultiplyTwoNumbers(int Num1, int Num2)
        {
            return Num1 * Num2;
        }
    }
}
 
The Output is:
 
img
 
Practical Example (Interface):
 
testInterface.cs
 
using System;
using System.Collections.Generic;
using System.Text;

namespace testInterface
{
    interface IParentInterface
    {
        void ParentInterfaceMethod();
    }

    interface IMyInterface : IParentInterface
    {
        void MethodToImplement();
    }

    class Program : IMyInterface
    {
        static void Main(string[] args)
        {
            Program iImp = new Program();
            iImp.MethodToImplement();
            Console.ReadLine();
            iImp.ParentInterfaceMethod();
            Console.ReadLine();
        }

        public void MethodToImplement()
        {
            Console.WriteLine("MethodToImplement() called.");
        }

        public void ParentInterfaceMethod()
        {
            Console.WriteLine("ParentInterfaceMethod() called.");
        }
    }
}
 
The Output is:
 
img