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
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'>
|
|
|
|

|
|
|
|
</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.
|
|
|