The basics
In C# you might some times want to have a general rule for several classes. You don’t want them to share the exact same code, but the same elements. An interface sets up rules for a inheriting class by telling it what it has to implement as a minimum in order to avoid compilation errors.
An example:
interface IPerson
{
string Name{get; set;}
int Age{get;set;}
int YearsToRetirement();
}
That’s it, that is what an interface looks like. There is no implementation in an interface, as can be seen in the above example. The only value of the interface is to guarantee that the inheriting classes all have these properties and methods.
There are several reasons why it’s good to to use an interface. One of them is that it helps a user to understand the class. If a class inherits the interface
IDispose then the user of that class knows that it has the Dispose() method.
Another reason is that an Interface can be used as a type to create general variables or method parameters. More about this soon.
Rules for interfaces
There are several rules for an interface that you have to consider.
Some are optional, some are not.
An interface should be named in the following fashion: Start with a capitol i(I) and follow by a name starting with a capital letter.
Examples:
IPerson, IDisposable, IEnumerable
An interface can only contain the following members:
- Methods
- Properties
- Events
- Indexers
A class can inherit as many interfaces as it wants(
whereas it can only inherit one other class)
All members of a class are automatically public(as is the interface).
A class that inherits an interface automatically inherits the member signatures and
must implement them.
A class can inherit an interface multiple times(by inheriting other classes or interfaces that inherit that interface). If so, implementation of the interface members only has to occur once. If a method has already been implemented it cannot be implemented again, however it
can be overridden if the original implementation was declared virtual.
An interface can be inherited by another interface.
An interface cannot be instantiated.
Next page: An example on how to use interfaces in more detail.
[breakpoint]
Example
Let’s create an interface for a person. The reason we’ll create an interface instead of a root class is that we’ll have a method called
YearsToRetirement() that will calculate the years a person has left to retire. We will use this method for various kinds of people such as a student and a professor. The difference between these two characters will be that a student won’t be allowed to studdy if he/she is a senior citizen whereas a professor might be retired but still teaching. If a professor is retired but still studdying we want a check. This will make the two methods similar but not identical, so we just want to announce that there will be a method, but not tell how to implement it.
interface IPerson
{
string Name{get; set;}
int Age{get;set;}
int YearsToRetirement();
}
This interface tells an inheriting class that it must implement two properties,
Name and
Age and a method,
YearsToRetirement(). A class kan have more members, but these are the minimum requirements. A student class could look like this:
public class Student : IPerson
{
private string name;
private int age;
private bool partTimeJob;
public string Name
{
get{return name;}
set{name = value;}
}
public int Age
{
get{return age;}
set{age = value;}
}
public bool PartTimeJob
{
get{return partTimeJob;}
set{partTimeJob = value;}
}
public int YearsToRetirement()
{
return 65 - age;
}
}
As we can see, the class
Student inherits
IPerson just as it would inherit a class. It then creates properties for
Name and
Age, but also
PartTimeJob which is specific for the student class and tells if a student object has a part time job.
If we hadn’t implemented all the interface members, say the property
Name, an error would have been thrown when compiling the project:
'baluba.Student' does not implement interface member 'baluba.IPerson.Name' (CS0535)
(baluba being the namespace)
We can se that our
YearsToRetirement method in our
Student class calculates the age of retirements minus the current age of the student.
Let’s create a Professor object:
public class Professor : IPerson
{
private string name;
private int age;
private bool hasBeard;
public string Name
{
get{return name;}
set{name = value;}
}
public int Age
{
get{return age;}
set{age = value;}
}
public bool HasBeard
{
get{return hasBeard;}
set{hasBeard = value;}
}
public int YearsToRetirement()
{
int i = 65 - age;
if (i < 0) {
i = 0;
}
return i;
}
This class also implements the
IPerson interface, but differs from student in the
YearsToRetirement method. This is because a professor could be more than 65 years old and still be a professor. In such an event, we don’t want years left to be negative(that would be silly), we just want to return 0 and let the user handle it.
The
Professor class also has it’s own specific property,
HasBeard, as we can see, and it’s not part of the
IPerson interface. Otherwize,
Professor implements all the members of
IPerson and will compile.
Now, let’s use this:
class Program
{
public static void Main(string[] args)
{
Student s = new Student();
s.Name = "Johan";
s.Age = 25;
s.PartTimeJob = true;
Professor p = new Professor();
p.Name = "Roger";
p.Age = 63;
p.HasBeard = true;
}
}
}
What we can see in this example is that we create two objects,
Student s and
Professor p. Now we want to print their name, age and time left to retirement. One way would be to create two methods, one that prints student objects and one that prints professor objects. This would however mean that we would have two identical methods that would only differ on the method input parameter. Here is a great opportunity to show the power of interfaces:
public static void PrintPerson(IPerson ip)
{
Console.WriteLine("Name: {0}\nAge: {1}\nYears to retirement: {2}", ip.Name, ip.Age, ip.YearsToRetirement());
}
When we send in an object to the
PrintPerson method it has to be an object that inherits the
IPerson interface. As mentioned earlier in this article interfaces can’t be instantiated, but they can be used as types. That’s what we do here. An
IPerson object has the properties
Name and
Age and the
YearsToRetirement method. It doesn’t matter that they are implemented in different ways, the whole point is that they mean the same thing and the methods return an int telling how long the object has to retirement.
This image illustrates how a student object is used
When sending a
Student object or a
Professor object to the
PrintPerson method we accept it as an
IPerson object which gives us access to the the members of
IPerson. This means that whatever future class that implements
IPerson can use the
PrintePerson method no matter how it’s designed since it has to implement all it’s members.
Here’s how it’s used in full:
class Program
{
public static void Main(string[] args)
{
Student s = new Student();
s.Name = "Johan";
s.Age = 25;
s.PartTimeJob = true;
Professor p = new Professor();
p.Name = "Roger";
p.Age = 63;
p.HasBeard = true;
PrintPerson(s);
Console.WriteLine("");
PrintPerson(p);
Console.Read();
}
public static void PrintPerson(IPerson ip)
{
Console.WriteLine("Name: {0}\nAge: {1}\nYears to retirement: {2}", ip.Name, ip.Age, ip.YearsToRetirement());
}
}
This would give the following output:
This concludes that the interface forces these two classes to implement all it’s members but does not tell how to implement them.
Now a person could look up the
IPerson interface and get an idea of what all classes that implements this interface contains (as minimum). That can clarify a lot and brings us to our next example which will shed more light on the use of interfaces.
Next page: An example on how our program can be reused in the future.
[breakpoint]
Example 2
The reason we want to implement the
IPerson interface is because we want to offer our programmers a possibility to plan for the future. If we later on want to create a new person, an employee, we don’t want to have to create a new method just to output the employee, we want to be able to reuse the
PrintPerson method. If the
Employee class implements the
IPerson interfaces it can, however, use the
PrintPerson method since it can identify
Employee as an
IPerson object.
public class Employee : IPerson
{
private string name;
private int age;
private string department;
private int sallary;
public string Name
{
get{return name;}
set{name = value;}
}
public int Age
{
get{return age;}
set{age = value;}
}
public string Department
{
get{return department;}
set{department = value;}
}
public int Sallary
{
get{return sallary;}
set{sallary = value;}
}
public int YearsToRetirement()
{
return 65 - age;
}
}
The
Employee class has several more properties but still has the basics of an
IPerson interface since it inherits the interface in question. This can now, without problem, be used in the
PrintPerson method:
class Program
{
public static void Main(string[] args)
{
Student s = new Student();
s.Name = "Johan";
s.Age = 25;
s.PartTimeJob = true;
Professor p = new Professor();
p.Name = "Roger";
p.Age = 63;
p.HasBeard = true;
Employee e = new Employee();
e.Name = "Marcus";
e.Age = 32;
e.Department = "Economy";
e.Sallary = 26000;
PrintPerson(s);
Console.WriteLine("");
PrintPerson(p);
Console.WriteLine("");
PrintPerson(e);
Console.Read();
}
public static void PrintPerson(IPerson ip)
{
Console.WriteLine("Name: {0}\nAge: {1}\nYears to retirement: {2}", ip.Name, ip.Age, ip.YearsToRetirement());
}
}
This would give the following output:
This illustrates how interfaces creates code that can easily be used by future classes as well.
Enjoy!