Home | Applications | Documentation | Transport | Personal |

Documentation

Home | Cloud | C# | Database | DevOps | Revision | Web |

C# | API | Delegates | Dll | Garbage | Events | Interfaces | Lambdas | MVC | NoSql | Objects | Tasks | Worker |

C# Interfaces

Introduction

This page is for the basics of interfaces and the notes on IEnumerable, IDisposible etc...

Basics

interface IPerson
{
string title { get; set; }
string firstName { get; set; }
string surname { get; set; }

string concatenateName();
}

To implement the interface, use...
class Person : IPerson

Example

Here is a small piece of code I created whilst looking at the SOLID designs pattern from the web
Create a class called HouseDetails
public class HouseDetails
{
public string firstName { get; set; }
public string lastName { get; set; }
public List<string> address { get; set; }
public string postcode { get; set; }
public int noBedrooms { get; set; }
public string roofType { get; set; }
public bool hasConservatory { get; set; }
}

Now create 2 interfaces, IHouseOwnership and IHouseBuilding
public interface IHouseOwnership
{
string firstName { get; set; }
string lastName { get; set; }
List<string> address { get; set; }
string postcode { get; set; }
}
public interface IHouseBuilding
{
int noBedrooms { get; set; }
string roofType { get; set; }
bool hasConservatory { get; set; }
}

Now add the interfaces to the HouseDetails class
public class HouseDetails : IHouseOwnership, IHouseBuilding

Now if we go to the main form and instantiate the classes, see what properties are available for the following objects.
HouseDetails house = new HouseDetails();
house.
IHouseOwnership owns = new HouseDetails();
owns.
IHouseBuilding build = new HouseDetails();
build.
Here you can see how the contracts on the interfaces can control the behaviour of the class.

Another example

Here is a small piece of code that shows that different classes that have the same interface, can be grouped into a List<>.
public interface ICarModel
{
string make { get; set; }
string model { get; set; }
IStoreVehicleData storeData { get; set; }
}
public class CarModel : ICarModel
{
public string make { get; set; }
public string model { get; set; }
public IStoreVehicleData storeData { get; set; } = new CarModelStore();
}
public class OddModel : ICarModel
{
public string make { get; set; }
public string model { get; set; }
public IStoreVehicleData storeData { get; set; } = new OddModelStore();
}

Code shows the data stored in a List of Interface CarModel (ICarModel)
List<ICarModel> cars = new List<ICarModel>();
cars.Add(new CarModel { make = "Ford", model = "Escort" });
cars.Add(new OddModel { make = "Talbot", model = "Horizon" });
cars.Add(new CarModel { make = "Ford", model = "Sierra" });

We can now loop through the List of ICarModel and write the information. We can also check what
type of class is stored and show data accordingly.
foreach (var car in cars)
{
vehicles.Add(car.storeData.storeData(car));
listBox1.Items.Add(car.make + ", " + car.model );
if (car is OddModel ocar)
{
listBox1.Items.Add("Yukkk");
}
}

IComparable

This interface contains only one method and this is CompareTo. This CompateTo method returns an integer value. It is used to compare objects after they have been sorted. If the value is less than 0 it indicates that this object should be placed before the one it is being compared with. If the value returned is zero, it indicates that the object should be placed at the same position as the one it is being compared with and if the value returned is greater than 0 it means that this object should be placed after the one it is being compared with.

Example. Please look at the Employee class. This class implements IComparable<Employee>, which means an Employee instance can be compared with other Employee instances. The Employee class has 2 instance properties: Salary and Name.
Also The Employee class provides the CompareTo(Employee) method and the ToString method.
CompareTo The method returns int—this indicates which Employee should be ordered first. We want to sort by the Salary from highest to lowest.
However If Salary is equal, we want to sort alphabetically. We check for equal Salary values, and in that case sort alphabetically.
Main Here we create a List of Employee instances. These are sorted first by Salary and then by Name.

See the example below.

using System;
using System.Collections.Generic;
class Employee : IComparable<Employee>
{
public int Salary { get; set; }
public string Name { get; set; }

public int CompareTo(Employee other)
{
// Alphabetic sort if salary is equal. [A to Z]
if (this.Salary == other.Salary)
{
return this.Name.CompareTo(other.Name);
}
// Default to salary sort. [High to low]
return other.Salary.CompareTo(this.Salary);
}

public override string ToString()
{
// String representation.
return this.Salary.ToString() + "," + this.Name;
}
}

class Program
{
static void Main()
{
List<Employee> list = new List<Employee>();
list.Add(new Employee() { Name = "Steve", Salary = 10000 });
list.Add(new Employee() { Name = "Janet", Salary = 10000 });
list.Add(new Employee() { Name = "Andrew", Salary = 10000 });
list.Add(new Employee() { Name = "Bill", Salary = 500000 });
list.Add(new Employee() { Name = "Lucy", Salary = 8000 });

// Uses IComparable.CompareTo()
list.Sort();

// Uses Employee.ToString
foreach (var element in list)
{
Console.WriteLine(element);
}
}
}

Results from the program..
500000,Bill
10000,Andrew
10000,Janet
10000,Steve
8000,Lucy

IDisposable

This interface ensures that you can tidy up an object when you have finished with it. IDisposable has a Dispose method that must be used. This is required as although the object will be removed from memory when the object goes out of scope, it is good practise to remove it when it's not required anymore. It is also a good place to remove any connections, file handles etc.
The using block does require that the object has the IDisposable interface.
Here is an example of it's use.

Person person = new Person();
person.title = txtTitle.Text;
person.firstName = txtFirstname.Text;
person.surname = txtSurname.Text;

When you add IDisploable to the class..
class Person : IPerson, IDisposable
You have the option to "Implement interface with Dispose pattern". This will create the code below (comments removed).

private bool disposedValue = false; // To detect redundant calls

protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}

You can now amend the code to use the "using" statement.

using (Person person = new Person())
{
person.title = txtTitle.Text;
person.firstName = txtFirstname.Text;
person.surname = txtSurname.Text;
txtFullName.Text = person.concatenateName();
}

IEnumerable

Programs spend a lot of time consuming lists and other collections. This is called iterating or enumerating.
Any C# object can implement the IEnumerator interface that allows other programs to get an enumerator from the object. The enumerator object can then be used to enumerate (or iterate) on the object.
The GetEnumerator method exposes the MoveNext and Reset methods, and the Current property. The code below shows the usage.

var fullNameString = txtFullName.Text.GetEnumerator();
while (fullNameString.MoveNext())
{
lstPeople.Items.Add(fullNameString.Current);
}

The IEnumerable interface needs the GetEnumerator method.

Besides looping over IEnumerable things, we can invoke extension methods upon them. System.Linq gives us many methods that act upon IEnumerables.

class Program
{
static void Main()
{
// Part 1: query expression.
IEnumerable result = from value in Enumerable.Range(0, 5)
select value;

// Part 2: loop over IEnumerable.
foreach (int value in result)
{
Console.WriteLine($"IENUMERABLE: {value}");
}
}
}

In System.Linq, many extension methods that act upon IEnumerable are available. These are used in many C# programs.
Part A Here we see the Average extension, which computes the average value in an IEnumerable (like an int array).
Part B We can apply many transformations to an IEnumerable instance, including the ToList and ToArray conversions.

class Program
{
static void Main()
{
int[] values = new int[] { 2, 3, 1 };

// Part A: use Average extension method on IEnumerable.
double average = values.Average();
Console.WriteLine($"AVERAGE: {average}");

// Part B: convert IEnumerable with ToList extension.
List list = values.ToList();
Console.WriteLine($"TOLIST: {list.Count}");
}
}

Many classes implement IEnumerable. We can pass them directly to methods that receive IEnumerable arguments. The type parameter must be the same.

class Program
{
static void Main()
{
Display(new List { true, false, true });
}

static void Display(IEnumerable argument)
{
foreach (bool value in argument)
{
Console.WriteLine(value);
}
}
}

This example implements the IEnumerable interface on an Example class. The class contains a List, and for GetEnumerator, we use the List's GetEnumerator method.

class Example : IEnumerable
{
List _elements;

public Example(string[] array)
{
this._elements = new List(array);
}

IEnumerator IEnumerable.GetEnumerator()
{
Console.WriteLine("HERE");
return this._elements.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return this._elements.GetEnumerator();
}
}

class Program
{
static void Main()
{
Example example = new Example(
new string[] { "cat", "dog", "bird" });
// The foreach-loop calls the generic GetEnumerator method.
// ... It then uses the List's Enumerator.
foreach (string element in example)
{
Console.WriteLine(element);
}
}
}

Some methods, like Enumerable.Range(), make it easier to create IEnumerable collections. We do not need to create a separate array.

class Program
{
static void Main()
{
// Get IEnumerable from Enumerable.Range and loop over it.
foreach (int value in Enumerable.Range(100, 2))
{
Console.WriteLine("RANGE(0, 2): {0}", value);
}
}
}

Yield. This keyword is placed before "return." It is used in methods that return IEnumerable. We can use yield to "save the state" of the function after the return.
Safer, more robust. With IEnumerable, our code becomes safer and more robust. It does this by hiding all the details about the underlying collection.