Interfaces in C#

What is an Interface?

An interface is a named collection of semantically related abstract members.

An interface is like a class, but with three major differences:

The purpose of an interface is to define a common set of characteristics to be shared among many types, which will provide an ability to perform the same operations on these types - polymorphism.

 

Why not use an Abstract Base Class instead of an Interface?

 

Defining an Interface

public interface IPointy {

byte GetNumberOfPoints(); // Implicitly public and abstract

}

 

Implementing an Interface

public class Hexagon : Shape, IPointy {

public Hexagon(){ }

public Hexagon(string name) : base(name){ }

public byte GetNumberOfPoints(){return 6;}

}

public class Triangle : Shape, IPointy {

public Triangle(){ }

public Triangle(string name) : base(name){ }

public byte GetNumberOfPoints(){return 3;}

}

 

Invoking Interface Members at the Object Level

Hexagon hex = new Hexagon();

Console.WriteLine("Points: {0}", hex.GetNumberOfPoints());

 

Obtaining Interface References : Explicit Casting

//Safely cast for interface reference.

Circle c = new Circle("Circle 1");

IPointy itfPt;

try{

itfPt = (IPointy)c;

Console.WriteLine(itfPt.GetNumberOfPoints());

}

catch(InvalidCastException e){

Console.WriteLine("OOPS! Not Pointy...");

}

 

Obtaining Interface References: The "as" Keyword

// Second way to test for an interface.

Hexagon hex = new Hexagon("Hexagon 2");

IPointy itfPt;

itfPt = hex as IPointy;

if (itfPt != null)

Console.WriteLine(itfPt.GetNumberOfPoints());

else

Console.WriteLine("OOPS! Not Pointy...");

 

Obtaining Interface References: The "is" Keyword

// Third way to test for an interface.

Triangle t = new Triangle();

if (t is IPointy)

Console.WriteLine(t.GetNumberOfPoints());

else

Console.WriteLine("OOPS! Not Pointy...");

 

Interfaces with Multiple Base Interfaces

interface IBasicCar {

void Drive(); }

interface IUnderWaterCar {

void Dive(); }

interface IJamesBondCar : IBaseCar, IUnderWaterCar {

void TurboBoost(); }

 

public class JBCar : IJamesBondCar {

public JBCar() {}

void IBasicCar.Drive() {

Console.WriteLine("Speeding up..."); }

void IUnderWaterCar.Dive() {

Console.WriteLine("Submerging..."); }

void IJamesBondCar.TurboBoost() {

Console.WriteLine("Blast off!"); }

}

 

JBCar j = new JBCar();

if (j is IJamesBondCar) {

((IJamesBondCar)j).Drive();

((IJamesBondCar)j).TurboBoost();

((IJamesBondCar)j).Dive();

}

 

Exploring the System.Collections Namespace

Interfaces of System.Collections
Interface Name Meaning in Life
ICollection Defines generic characteristics (e.g., read-only, thread safe, etc.) for a collection class
IComparer Allows two object to be compared
IDictionary Allows an object to represent its contents using name/value pairs
IDictionaryEnumerator Used to enumerate the contents of an object supporting IDictionary
IEnumerable Returns the IEnumerator interface for a given object
IEnumerator Generally used to support foreach-style iteration of subtypes
IHashCodeProvider Returns the hash code for the implementing type using a customized hash algorithm
IList Provides behavior to add, remove, and index items in a list of objects

 

Most of the classes defined within System.Collections namespace implement these interfaces to provide access to their contents.

Classes of System Collections
Class Name Key Implemented Interfaces
ArrayList IList, ICollection, IEnumerable, and ICloneable
Hashtable IDictionary, ICollection, IEnumerable, and ICloneable
Queue ICollection, ICloneable, and IEnumerable
SortedList IDictionary, ICollection, IEnumerable, and ICloneable
Stack ICollection and IEnumerable

 

Callback Interfaces

Interfaces are commonly used as a callback mechanism.

// The callback interface.

public interface IEngineEvents{

void AboutToBlow(string msg);

void Exploded(string msg);

}

Event interfaces are not typically implemented directly by the client executable, but rather by a helper sink object, upon which the sender of the events will make calls.

//Car event sink.

public class CarEventSink : IEngineEvents {

private string name; // Diagnostic member to identify sink.

public CarEventSink() {}

public CarEventSink(string sinkName) {

name = sinkName; }

public void AboutToBlow(string msg) {

Console.WriteLine("{0} reporting: {1}", name, msg); }

public void Exploded(string msg) {

Console.WriteLine("{0} reporting: {1}", name, msg); }

}

Next task is to pass a reference to this sink into the Car type. The Car holds onto the reference, and makes calls back on the sink when appropriate.

public class Car {

...

// The set of connected sinks

ArrayList itfConnections = new ArrayList();

// Attach or disconnect from the source of events

public void Advise(IEngineEvents itfClientImpl) {

itfConnections.Add(itfClientImpl); }

public void Unadvise(IEngineEvents itfClientImpl) {

itfConnections.Remove(itfClientImpl); }

public void SpeedUp(int delta) {

// If the car is dead, send exploded event to each sink.

if(carIsDead) {

foreach(IEngineEvents e in itfConnections)

e.Exploded("Sorry, this car is dead...");

}

else {

currSpeed += delta;

// Dude, you're almost dead! Proceed with caution!

if(10 == maxSpeed - currSpeed) {

foreach(IEngineEvents e in itfConnections)

e.AboutToBlow("Careful buddy! Gonna blow!");

}

if(currSpeed >= maxSpeed)

carIsDead = true;

else

Console.WriteLine("\tCurrSpeed = {0} ", currSpeed);

}

}

Client-side code.

//Make a car and listen to the events.

public class CarApp {

public static int Main(string[] args) {

Car c1 = new Car("SlugBug", 100, 10);

// Make sink object.

CarEventSink sink = new CarEventSink();

// Pass the Car a reference to the sink.

c1.Advise(sink);

// Speed up (this will generate the events).

for(int i = 0; i < 6; i++)

c1.SpeedUp(20);

// Detach from events.

c1.Unadvise(sink);

return 0;

}

}