--- marp: true paginate: true math: mathjax theme: buutti title: 9. Interfaces --- # Interfaces ## Overview * Interfaces * Interfaces or Inheritance? ## Interfaces * In addition to abstract classes, *__interfaces__* are a way to achieve abstraction to your program * Interfaces are classes that have *__no internal functionality__* * Interfaces describe the methods and properties that a class has to have when ***implementing*** the interface * Think of it as a ***contract***: by implementing an interface, the class **_has to use_** all the methods and properties defined in the interface * As with [abstract classes](8-inheritance-and-abstract-classes.md#abstract-classes), interfaces cannot be instantiated directly * (It wouldn't make any sense as interfaces have no implementation) * Interfaces are way more commonly used than abstract classes ### Creating an interface * Define an interface using the interface keyword instead of class: ```csharp interface IUser { int Id { get; set; } string Name { get; set; } void GetUserStatistics(); } ``` * [Interface names should begin](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces) with the capital letter `I` to more easily identify them as interfaces and not classes * Notice that interfaces can contain both properties and methods ***but not fields*** * Methods are declared without the method body (no implementation) * The methods are implemented on the classes that uses the interface ### Implementing an interface * Implement an interface just like you would inherit a class: ```csharp class User : IUser { } ``` * The compiler will now throw a bunch of errors saying that the User class does not implement the properties and methods defined in the interface * Let's fix that by defining those next --- ```csharp class User : IUser { public int Id { get; set; } public string Name { get; set; } public void GetUserStatistics() { // Some code here } } ``` * The interface is now fully implemented and the compiler is happy * The interface does not dictate **_how_** the methods are implemented, those just need to be implemented * ***Note:*** To quickly implement the interface, click the IUser interface name and click _💡 > Implement interface_. ### Implementing multiple interfaces * Unlike with inheritance, classes can implement multiple interfaces * In contrast, classes can only inherit from one base class * This is done by separating the interfaces with a comma: ```csharp class Rock : IPickable, IThrowable, ICrushable { // Code that implements IPickable, IThrowable and ICrushable } ``` ### Why use interfaces? * Interfaces allow common functionality among classes that are otherwise unrelated to each other * In those cases, inheriting from some shared base class wouldn't make sense * Consider the following example: ```csharp public void DeleteData(IDeletable data) { data.Delete(); } ``` * The above method accepts **_any_** type of object that implements `IDeletable`, regardless of other functionality ## Interface or Inheritance? ### Example 1
| `class Dog` | `class Human` | `class Bear` | |:------------|:----------------|:--------------| | `Eat()` | `Eat()` | `Eat()` | | `Sleep()` | `Sleep()` | `Sleep()` | | `WagTail()` | `Contemplate()` | `Hibernate()` |
* All classes could inherit from a base class `Animal`, which has methods `Eat()` and `Sleep()` * The question is, should the base class be abstract? * Depends on your program: do you ever need an object that can ***only*** `Eat()` and `Sleep()`? ### Example 2
| `class Tree` | `class Human` | `class Car` | |:--------------------|:--------------|:------------| | `Grow()` | `Grow()` | `Explode()` | | `Photosynthesize()` | `Move()` | `Move()` |
* It wouldn't make sense to use inheritance here, since there is no functionality that is shared between ***all*** classes * Instead you could make two interfaces: `IGrowable` and `IMovable` ## Interfaces: An example * Let's make a program that has two lists: one for all objects that can move (`IMovable`) and one for all objects that can be carried (`ICarryable`) * Finally, every movable object is moved and carryable object is carried ---
```csharp interface IMovable { void Move(); } interface ICarryable { void Carry(); } ```
```csharp class Elephant : IMovable { public void Move() { Console.WriteLine("The elephant is moving'"); } } class Cat : IMovable, ICarryable { public void Move() { Console.WriteLine("The cat is moving'"); } public void Carry() { Console.WriteLine("You are carrying the cat"); } } class Rock : ICarryable { public void Carry() { Console.WriteLine("You are carrying the rock"); } } ```
---
```csharp class Program { static void Main(string[] args) { Elephant elephant = new Elephant(); Cat cat = new Cat(); Rock rock = new Rock(); List movables = new List{ elephant, cat }; List carryables = new List{ cat, rock }; foreach (IMovable movable in movables) movable.Move(); foreach (ICarryable carryable in carryables) carryable.Carry(); } } ```
![](imgs/9%20Interfaces_1.png)
## Exercise 1: A web of relations Create a console application that has an interface `IInfo` and two classes `Product` and `Category` that both implement `IInfo`. 1) Inside `IInfo`, declare a property `InfoText` and a method `PrintInfo` 2) Implement both the property and method in `Product` and `Category` 3) Initialize a couple of products and categories, with info texts of your choice 4) Create a list of type `IInfo`, and add the products and categories to it 5) Create a main loop, where each time the user presses enter, all the info texts inside the list are printed ## Exercise 2: The `IComparable` Interface Create a program that sorts a list of shapes by area, using the [IComparable](https://docs.microsoft.com/en-us/dotnet/api/system.collections.icomparer?view=netcore-3.1) interface which is used by the `List.Sort()` method to know whether the elements should come before or after each other in the list. 1) Start by creating 4 classes: `Shape`, `Square`, `Triangle`, and `Circle`. `Square`, `Triangle` and `Circle` inherit from `Shape`, which implements the `IComparable` interface. 2) Shape has a public property `double Area`. `Square`, `Triangle` and `Circle` have to have constructors to calculate the area: `Square` and `Triangle` with `length` and `height`, and `Circle` with `radius`. --- 3) `The CompareTo(Shape? other)` method should return `1` if the area is greater than what is being compared with (`Shape other`), `0` if the areas are equal, and `-1` if the area is smaller. 4) Try out your solution by creating a couple of squares, triangles and circles in a list of shapes, and sorting the list with the `.Sort()` method. 5) Print the areas in the sorted array with a `foreach` loop. They should be printed in an increasing order.