Introduction
This page is for the basics of interfaces and the notes on IEnumerable, IDisposible etc...
Home | Applications | Documentation | Transport | Personal |
C# | API | Delegates | Dll | Garbage | Events | Interfaces | Lambdas | MVC | NoSql | Objects | Tasks | Worker |
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
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
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
}
static void Display(IEnumerable
{
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
public Example(string[] array)
{
this._elements = new List
}
IEnumerator
{
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.