c# – The Interface

We previously came across the concept of inheritance, which is where a child class (implicitly) inherits the properties and methods from it’s parent class.

However what if you don’t want a class to inherit methods and properties, but we do want class to have certain members (properties and methods) with particular names. That’s where “interfaces” comes in.

If you want to create a collection of classes and you want them all to have methods of certain names (although the content of the methods can be completely different), then you use interfaces.

A common example of where interfaces comes in handy is when you create a class (e.g. called DBclass) that’s designed to interact with a db and perform crud operations. For this class you might have a method called “Read()” for retrieving data from db, and another method called “Write()” for writing data to db.

Now lets says you have another class (e.g. called XMLclass), which this time is designed to interact with an xml file, and perform crud operation. Both these classes do the same job, the only difference being that they have different data sources, one is a db, and the other is an xml file. So it would be best practice for consistency purposes that both these classes have methods of the same names. Hence XMLclass should also contain methods called Read() and Write().

To make sure this standard and consistency gets enforced, we create an interface. An interface is basically a template (aka specification) that we use to create classes from. These classes that get created from a interface, must contain the methods an properties that have the same name as those specified in the interface.

The interface works on the basis of “minimum-requirements”. This means that a class can have many more methods/properties specific to the class, but still comply with the interface as long as it includes the methods and properties that have names that match with those listed in the interface.

Here’s an example, in this example we have a class called “Transactions”, and this class is designed to comply with an interface called “Itransactions”:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace InterfaceApplication
{

	// Here we define the interface using the "interface" keyword
   public interface ITransactions
   {
      // Here we have defined a property called "country". This means that all classes that conforms
	  // to this interface, must also have a string property called country (with read/write enabled)
	  string country {get; set;}

      // Here we say that all classes that are created to conform with this interface, must contain at least 2 methods.
// one of these methods must have the name "showTransaction" (which has a void output parameter) and another
	  // method with the name "getAmount" (which has a double output parameter)
	  // note: due to purpose of the interface, the interface, the names of the methods has to be, and not 
// what task the method actually performs. That info is defined within the complying class. 
      void showTransaction();
      double getAmount();
   }
   // Note, you don't prefix any of the above items with "public", that is the default anyway, and you
   // cannot change this either.

   // Here we are creating a new class called "Transactions", and on the
   // first line we have specified that this class must conform to the
   // ITransactions interface.
   public class Transaction : ITransactions
   {
		// here we declared the "country" in order to comply with the interface's requirements.
		public string country {get; set;}

		// The interface, defines what is essentially "minimum requirements". That means
		// that we can add extra properties/methods to our class on top of those that we
		// must include according to the interface. Here we have added 3 additional
		// properties:
		public string TCode{get; set;}
		public string Date{get; set;}
		public double Amount{get; set;}

	    // here is the constructor.
		public Transaction(string c, string d, double a)
		{
		this.TCode = c;
		this.Date = d;
		this.Amount = a;
		this.country = "England";
		}

		// Here we created a method called "getAmount" in accordance with
		// the interface's requirements.
		public double getAmount()
		{
		return Amount;		// The content of the method doesn't matter. As long as method by this
							// name exists.
		}

		// Here we created a method called "showTransaction" in accordance with the interface's
		// requirements.
		public void showTransaction()
		{
		Console.WriteLine("Transaction: {0}", TCode);
		Console.WriteLine("Date: {0}", Date);
		Console.WriteLine("Amount: {0}", getAmount());

		}

		// This is an additional method that the interface doesn't require. However
		// we haved include this as it is a method we want in this class.
		public void CountryOfTransaction()
		{
			Console.WriteLine("This transaction took place in " + country);
		}

   }

   class Program
   {
      static void Main(string[] args)
      {
         Transaction t1 = new Transaction("002", "9/10/2012", 451900.00);
         t1.showTransaction();
		 t1.CountryOfTransaction();
      }
   }
}

This outputs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InterfaceDemo
{
    // Here we have defined the interface. On this occasion
    // we are saying that all complying class must have at least
    // 2 methods, where 2 of those methods have the names "Read" and 
    // "Write"
    public interface DataSourceInteractionTemplate
    {

        string Read();

        void Write(object objectdata);
    }



    public class DBInteraction : DataSourceInteractionTemplate
    {

        // this is an additional property. 
        public string HelloDBMessage { get; set; }

        // Here we created a method called "Read" in accordance with 
        // the DataSourceInteractionTemplate interface
        public string Read()
        {
            Console.WriteLine("reading data from database");
            return "test";
        }


        // Here we created a method called "Write" in accordance with 
        // the DataSourceInteractionTemplate interface
        public void Write(object InputData)
        {
            Console.WriteLine("writing data to database");
        }

        // this is an additional method. 
        public void HelloDBMethod()
        {
            this.HelloDBMessage = "Hello Database";
            Console.WriteLine(this.HelloDBMessage);
        }

    }


    public class XMLInteraction : DataSourceInteractionTemplate
    {

        // this is an additional property. 
        public string HelloXMLMessage { get; set; }

        // Here we created a method called "Read" in accordance with 
        // the DataSourceInteractionTemplate interface
        public string Read()
        {
            Console.WriteLine("reading data from xml file");
            return "test";
        }

        // Here we created a method called "Write" in accordance with 
        // the DataSourceInteractionTemplate interface
        public void Write(object InputData)
        {
            Console.WriteLine("writing data to xml");
        }

        // this is an additional method. 
        public void HelloXMLMethod()
        {
            this.HelloXMLMessage = "Hello XML";
            Console.WriteLine(this.HelloXMLMessage);
        }
        
    }


    class Program
    {
        static void Main(string[] args)
        {
            DBInteraction DBobject = new DBInteraction();
            DBobject.Read();
            DBobject.Write(null);
            DBobject.HelloDBMethod();

            XMLInteraction XMLobject = new XMLInteraction();
            XMLobject.Read();
            XMLobject.Write(null);
            XMLobject.HelloXMLMethod();



        }
    }
}

The above will output:

So far we have seen that a interface gives some high-level rules that complying class must follow. Due to the nature of an interface, you cannot instantiate a object from a interface, since interface isn’t really a class. However, you can convert an existing object (that got instantiated by a complying class) to belong to that interface. When you do this, the object can still access it’s original method/properties, but only those that are named in the interface.

The cool advantage of this is that you can have an object that belongs to “DataSourceInteractionTemplate” interface, and use it’s read/write methods, without knowing whether you are interacting with a db,xml file, or something else!

Here’s an example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InterfaceDemo
{
    // Here we have defined the interface. On this occasion
    // we are saying that all complying class must have at least
    // 2 methods, where 2 of those methods have the names "Read" and 
    // "Write"
    public interface DataSourceInteractionTemplate
    {

        string Read();

        void Write(object objectdata);
    }



    public class DBInteraction : DataSourceInteractionTemplate
    {

        // this is an additional property. 
        public string HelloDBMessage { get; set; }

        // Here we created a method called "Read" in accordance with 
        // the DataSourceInteractionTemplate interface
        public string Read()
        {
            Console.WriteLine("reading data from database");
            return "test";
        }


        // Here we created a method called "Write" in accordance with 
        // the DataSourceInteractionTemplate interface
        public void Write(object InputData)
        {
            Console.WriteLine("writing data to database");
        }

        // this is an additional method. 
        public void HelloDBMethod()
        {
            this.HelloDBMessage = "Hello Database";
            Console.WriteLine(this.HelloDBMessage);
        }

    }


    public class XMLInteraction : DataSourceInteractionTemplate
    {

        // this is an additional property. 
        public string HelloXMLMessage { get; set; }

        // Here we created a method called "Read" in accordance with 
        // the DataSourceInteractionTemplate interface
        public string Read()
        {
            Console.WriteLine("reading data from xml file");
            return "test";
        }

        // Here we created a method called "Write" in accordance with 
        // the DataSourceInteractionTemplate interface
        public void Write(object InputData)
        {
            Console.WriteLine("writing data to xml");
        }

        // this is an additional method. 
        public void HelloXMLMethod()
        {
            this.HelloXMLMessage = "Hello XML";
            Console.WriteLine(this.HelloXMLMessage);
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            List<DataSourceInteractionTemplate=> ListOfDataObjects = new List<DataSourceInteractionTemplate=>();
            
            DBInteraction object1 = new DBInteraction();
            ListOfDataObjects.Add(object1); // this converts object1's type from DBInteraction to DataSourceInteractionTemplate. 
            XMLInteraction object2 = new XMLInteraction();
            ListOfDataObjects.Add(object2); // this converts object2's type from XMLInteraction to DataSourceInteractionTemplate.
            DBInteraction object3 = new DBInteraction();
            ListOfDataObjects.Add(object3);
            XMLInteraction object4 = new XMLInteraction();
            ListOfDataObjects.Add(object4);


            foreach (DataSourceInteractionTemplate databoject in ListOfDataObjects)
            {
                databoject.Read();
                databoject.Write(null);
                // DBobject.HelloDBMethod();   // the converted object can no longer access the class's members.  
                // XMLobject.HelloXMLMethod(); // except for the ones named in the interface specs.  
            }
            
            
        }
    }
}

The above shows how you can standardize interaction with many different data-sources by using interfaces. This means that objects originating from different classes are doing the right thing in the loop. This means they are behaving polymorphically.