Interfaces 2020
An interface is similar to a class, but it provides a specification rather than an implementation for its members.
An interface is special in the following ways:
- A class can implement multiple interfaces. In contrast, a class can inherit from only a single class.
- Interface members are all implicitly abstract. In contrast, a class can provide both abstract members and concrete members with implementations.
- Structs can implement interfaces. In contrast a struct cannot inherit from a class.
The interface type seems very similar to an abstract base class. When a class is market as abstract, it may define any number of abstract members to provide a polymorphic interface to all derived types. But even when a class type does define a set of abstract members, it may define any number of constructors, field data, nonabstract members, and so on. In contrast, interfaces only contain abstract members.
The polymorphic interface established by an abstract parent class suffers from one major limitation in that only derived types support the members defined by the abstract parent. But in larger software, it is very common to develop multiple class hierarchies that have no common parent beyond System.Object. Because abstract members in an abstract base class only apply to derived types, we have no way to configure types in different hierarchies to support same polymorphic interface.
Let's take a look at the following example:
abstract class ColoneableType { public abstract object Clone(); }
Only the members that extend CloneableType can support the Clone() method. If we create a new class that does not extend this base class, we cannot gain this polymorphic interface. This is one of the areas that the interface types can be useful. Once an interface has been defined, it can be implemented by any type, in any hierarchy, which any namespaces or any assembly. So, we can say that interfaces are highly polymorphic.
Consider the standard .NET interface IColneable defined in the Seemingly unrelated types such as System.Array and System.Data et al. are all implementing the ICloneable interface. Although these types have no common parent other than System.Object, we can treat them polymorphically via the ICloneable interface type. Out we get: In the example, we have a method CloneMe() that took an ICloneable interface parameter. So, we could pass this method any object that support that interface. Interface declaration is like a class declaration, but it provides no implementation for its members, since all its members are implicitly abstract. These members will be implemented by the classes and structs that implement the interface. In interface can contain only methods, properties, event, and indexers, which noncoincidentally are precisely the members of a class that can be abstract. Let's start with an example. When we define interface members, we do not define an implementation for the member. Interfaces are just a protocol, and never define an implementation. So, the following version of IPoint gives compile errors: Interfaces are nothing more than a named collection of abstract members. So, we cannot allocate interface type as we would a class or a structure: Interfaces are quite useless on their own. They don't bring much to the table until they are implemented by a class or structure. Here is the shapes hierarchy with interfaces we're going to use. Here are files for the class hierarchy diagram: Now we have a set of types that support IPoint interface. Next question is how we interact with the new interface. Let's invoke the methods of interface directly from the object level. First, look at the following Main() method: Output from the run is: That works well because the Hexagon class implements the IPoint interface and it has a Points property. However, in cases when we have lots of Shape-compatible types, and only some of which support IPoint, there is a chance to get an error if we invoke the Point property on a class that has not implemented IPoint. So, the natural question is how can we dynamically determine the set of interfaces that support IPoint? One way to determine at runtime whether a type supports a specific interface is to use an explicit case. In case when the type does not support the requested interface, we receive an InvalidCastException: Output is: Though the code above worked, it would be even better if we can determine which interfaces are supported before invoking the interface members in the first place. So, the next two sections will show the other ways of doing it. Another way of determining whether a class supports a given interface is to use as keyword. If the object can be treated as the specified interface, it returns a reference to the interface, if not, it returns a null reference. In this section, we're going to use is keyword. If the object is not compatible with the interface, the value false is returned. But if the type is compatible with the interface, we can safely call the members of the interface. Output is: We can construct methods that take interface as parameters. Here are some updates to our example: Here is the updated shapes hierarchy with interfaces we're going to use. If we define a method taking an IDraw3D interface as a parameter, we can send in any object implementing IDraw3D. Output from the run is: Note that the Triangle type is not drawing in 3D because it is not IDraw3D-compatible. Interfaces can be used as method return values. In the following example, we write a method that takes any System.Object, checks for IPoint compatibility, and then returns a reference to the extracted interface if supported. Output is:
public Interface ICloneable
{
object Clone();
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Interfaces
{
class Program
{
static void Main(string[] args)
{
string myString = "Hello Interfaces";
OperatingSystem MacOS = new OperatingSystem(PlatformID.MacOSX, new Version());
System.Data.SqlClient.SqlConnection sqlCon = new System.Data.SqlClient.SqlConnection();
CloneMe(myString);
CloneMe(MacOS);
CloneMe(sqlCon);
Console.ReadLine();
}
private static void CloneMe(ICloneable c)
{
object theClone = c.Clone();
Console.WriteLine("colne is a: {0}", theClone.GetType().Name);
}
}
}
colne is a: String
colne is a: OperatingSystem
colne is a: SqlConnection
public Interface IPoint
{
byte GetNumberOfPoints();
}
public Interface IPoint
{
// Error! Interfaces cannot have fields.
public int numbOfPoints;
// Error! Interface don't have constructors.
public IPoint() { numberOfPoints = 0; }
// Error! Interface don't provide an implementation.
byte GetNumberOfPoints() { return numbOfPoints; }
}
static void Main(string[] args)
{
// Error!
IPoint ptr = new IPoint();
}
// IPoint.cs
namespace CustomInterface
{
// The IPoint behavior as a read-only property
public interface IPoint
{
byte Points { get; }
}
}
// Circle.cs
using System;
namespace CustomInterface
{
public class Circle : Shape
{
public Circle() { }
public Circle(string name) : base(name) { }
public override void Draw()
{
Console.WriteLine("Drawing {0} the Circle", shapeName);
}
}
}
// ThreeDCircle
using System;
namespace CustomInterface
{
public class ThreeDCircle : Circle
{
public void Draw()
{
Console.WriteLine("Drawing a 3D Circle");
}
}
}
// Triangle.cs
using System;
namespace CustomInterface
{
public class Triangle : Shape, IPoint
{
public Triangle() { }
public Triangle(string name) : base(name) { }
public override void Draw()
{
Console.WriteLine("Drawing {0} the Triangle", PetName);
}
public byte Points
{
get { return 3; }
}
}
}
// Hexagon.cs
using System;
namespace CustomInterface
{
public class Hexagon : Shape, IPoint
{
public Hexagon() { }
public Hexagon(string name) : base(name) { }
public override void Draw()
{
Console.WriteLine("Drawing {0} the Hexagon", PetName);
}
public byte Points
{
get { return 6; }
}
}
}
// Shape.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CustomInterface
{
public abstract class Shape
{
protected string shapeName;
public Shape()
{
shapeName = "NoName";
}
public Shape(string s)
{
shapeName = s;
}
public virtual void Draw()
{
Console.WriteLine("Shape.Draw()");
}
public string PetName
{
get { return shapeName; }
set { shapeName = value; }
}
static void Main(string[] args)
{
}
}
}
static void Main(string[] args)
{
Hexagon hex = new Hexagon();
Console.WriteLine("Points: {0}", hex.Points);
Console.ReadLine();
}
Points: 6
static void Main(string[] args)
{
Hexagon hex = new Hexagon();
Console.WriteLine("Points: {0}", hex.Points);
Circle cir = new Circle("Voldemort");
IPoint ptr = null;
try
{
ptr = (IPoint)cir;
Console.WriteLine(ptr.Points);
}
catch (InvalidCastException e)
{
Console.WriteLine(e.Message);
}
Console.ReadLine();
}
Points: 6
Unable to cast object of type 'CustomInterface.Circle' to type 'CustomInterface.
IPoint'.
static void Main(string[] args)
{
Hexagon hex = new Hexagon("Voldemort");
IPoint pHex = hex as IPoint;
if (pHex != null)
Console.WriteLine("Points: {0}", hex.Points);
else
Console.WriteLine("Hexagon: IPoint not implemented");
Console.ReadLine();
}
static void Main(string[] args)
{
Shape[] s = { new Hexagon(), new Circle(),
new Triangle("Hermione"), new Circle("Ron")};
for (int i = 0; i < s.Length; i++)
{
s[i].Draw();
if (s[i] is IPoint)
Console.WriteLine("Points: {0}", ((IPoint)s[i]).Points);
else
Console.WriteLine("{0} is not support IPoint", s[i].PetName);
}
Console.ReadLine();
}
Drawing NoName the Hexagon
Points: 6
Drawing NoName the Circle
NoName is not support IPoint
Drawing Hermione the Triangle
Points: 3
Drawing Ron the Circle
Ron is not support IPoint
// IDraw3D.cs
using System;
namespace CustomInterface
{
public interface IDraw3D
{
void Draw3D();
}
}
// Circle.cs
using System;
namespace CustomInterface
{
public class Circle : Shape, IDraw3D
{
public Circle() { }
public Circle(string name) : base(name) { }
public override void Draw()
{
Console.WriteLine("Drawing {0} the Circle", shapeName);
}
public void Draw3D()
{
Console.WriteLine(" Drawing Circle in 3D");
}
}
}
// Hexagon.cs
using System;
namespace CustomInterface
{
public class Hexagon : Shape, IPoint, IDraw3D
{
public Hexagon() { }
public Hexagon(string name) : base(name) { }
public override void Draw()
{
Console.WriteLine("Drawing {0} the Hexagon", PetName);
}
public void Draw3D()
{
Console.WriteLine(" Drawing Hexagon in 3D");
}
public byte Points
{
get { return 6; }
}
}
}
// Shape.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CustomInterface
{
public abstract class Shape
{
protected string shapeName;
public Shape()
{
shapeName = "NoName";
}
public Shape(string s)
{
shapeName = s;
}
public virtual void Draw()
{
Console.WriteLine("Shape.Draw()");
}
public string PetName
{
get { return shapeName; }
set { shapeName = value; }
}
static void DrawIn3D(IDraw3D i3d)
{
Console.WriteLine("Drawing IDraw3D compatible type");
i3d.Draw3D();
}
static void Main(string[] args)
{
Shape[] s = { new Hexagon(), new Circle(),
new Triangle(), new Circle("Hermione")};
for (int i = 0; i < s.Length; i++)
{
s[i].Draw();
if (s[i] is IDraw3D)
DrawIn3D((IDraw3D)s[i]);
}
Console.ReadLine();
}
}
}
Drawing NoName the Hexagon
Drawing IDraw3D compatible type
Drawing Hexagon in 3D
Drawing NoName the Circle
Drawing IDraw3D compatible type
Drawing Circle in 3D
Drawing NoName the Triangle
Drawing Hermione the Circle
Drawing IDraw3D compatible type
Drawing Circle in 3D
// Shape.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CustomInterface
{
public abstract class Shape
{
protected string shapeName;
public Shape()
{
shapeName = "NoName";
}
public Shape(string s)
{
shapeName = s;
}
public virtual void Draw()
{
Console.WriteLine("Shape.Draw()");
}
public string PetName
{
get { return shapeName; }
set { shapeName = value; }
}
static void DrawIn3D(IDraw3D i3d)
{
Console.WriteLine("Drawing IDraw3D compatible type");
i3d.Draw3D();
}
static IPoint ExtractPointness(object o)
{
if (o is IPoint)
return (IPoint)o;
else
return null;
}
static void Main(string[] args)
{
int[] myInts = { 100, 200, 300 };
IPoint pRef = ExtractPointness(myInts);
if (pRef != null)
Console.WriteLine("Object has {0} points.", pRef.Points);
else
Console.WriteLine("This object does not implement IPoint.");
Console.ReadLine();
}
}
}
This object does not implement IPoint.
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization