You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
7.4 KiB
Markdown

---
marp: true
paginate: true
math: mathjax
theme: buutti
title: 9. Interfaces
---
# Interfaces
<!-- headingDivider: 5 -->
<!-- class: invert -->
## 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
<center>
| `class Dog` | `class Human` | `class Bear` |
|:------------|:----------------|:--------------|
| `Eat()` | `Eat()` | `Eat()` |
| `Sleep()` | `Sleep()` | `Sleep()` |
| `WagTail()` | `Contemplate()` | `Hibernate()` |
</center>
* 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
<center>
| `class Tree` | `class Human` | `class Car` |
|:--------------------|:--------------|:------------|
| `Grow()` | `Grow()` | `Explode()` |
| `Photosynthesize()` | `Move()` | `Move()` |
</center>
* 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
---
<div class='columns12' markdown='1'>
<div markdown='1'>
```csharp
interface IMovable
{
void Move();
}
interface ICarryable
{
void Carry();
}
```
</div>
<div markdown='1'>
```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");
}
}
```
</div>
</div>
---
<div class='columns21' markdown='1'>
<div markdown='1'>
```csharp
class Program
{
static void Main(string[] args)
{
Elephant elephant = new Elephant();
Cat cat = new Cat();
Rock rock = new Rock();
List<IMovable> movables =
new List<IMovable>{ elephant, cat };
List<ICarryable> carryables =
new List<ICarryable>{ cat, rock };
foreach (IMovable movable in movables)
movable.Move();
foreach (ICarryable carryable in carryables)
carryable.Carry();
}
}
```
</div>
<div markdown='1'>
![](imgs/9%20Interfaces_1.png)
</div>
</div>
## Exercise 1: A web of relations
<!--_class: "exercise invert" -->
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
<!--_class: "exercise invert" -->
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<Shape>` 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`.
---
<!--_class: "exercise invert" -->
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.