parent
76b49a6af8
commit
24ec8f9349
File diff suppressed because one or more lines are too long
@ -1,233 +1,198 @@
|
||||
# Static Members, Methods and Classes
|
||||
|
||||

|
||||
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
math: mathjax
|
||||
theme: buutti
|
||||
title: 10. Static Members, Methods and Classes
|
||||
---
|
||||
|
||||
# Static Members
|
||||
|
||||
So far, we have used __non-static__ fields in our classes
|
||||
|
||||
This means, that each instance of the class holds its own version of the field, and changing the value of it only affects that instance:
|
||||
|
||||
class MyAwesomeClass
|
||||
|
||||
{
|
||||
|
||||
public int MyProperty { get; set; }
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
MyAwesomeClass instance1 = new MyAwesomeClass();
|
||||
|
||||
MyAwesomeClass instance2 = new MyAwesomeClass();
|
||||
|
||||
instance1.MyProperty = 100;
|
||||
|
||||
instance2.MyProperty = 200; // instance1.MyProperty is still 100
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Static Members (continued)
|
||||
|
||||
Likewise, non-static class methods _have to _ be called through an instance:
|
||||
|
||||
class MyAwesomeClass
|
||||
|
||||
{
|
||||
|
||||
public void PrintText(string text) { Console.WriteLine(text); }
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
MyAwesomeClass instance = new MyAwesomeClass();
|
||||
|
||||
instance.PrintText("Hello World"); // Outputs "Hello World"
|
||||
|
||||
MyAwesomeClass.PrintText("Hello World"); // Results in an error
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
__Static fields are shared between all instances of a class__
|
||||
|
||||
Let's declare "MyProperty" property with the __static __ keyword. Now it can be referenced through the class type name, but not through the instance, as shown below:
|
||||
|
||||
class MyAwesomeClass
|
||||
|
||||
{
|
||||
|
||||
public static int MyProperty { get; set; } = 100;
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
MyAwesomeClass instance = new MyAwesomeClass();
|
||||
|
||||
Console.WriteLine(MyAwesomeClass.MyProperty); // Outputs "100"
|
||||
|
||||
Console.WriteLine(instance.MyProperty); // Results in an error
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# Static Members - Example
|
||||
|
||||
In this example, a static field is used for keeping count on how many times the class has been instantiated:
|
||||
|
||||
class Person
|
||||
|
||||
{
|
||||
|
||||
public static int totalPersons = 0;
|
||||
|
||||
private string name;
|
||||
|
||||
public Person(string personName) // Person Constructor
|
||||
|
||||
{
|
||||
|
||||
name = personName;
|
||||
|
||||
++totalPersons;
|
||||
|
||||
}
|
||||
|
||||
public void PrintInfo()
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("This person is called " + name + ".");
|
||||
|
||||
Console.WriteLine("There are " + totalPersons + " persons total.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Static Members - Example (continued)
|
||||
|
||||
Now let's instantiate a couple of persons and print their info:
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Person steve = new Person("Steve");
|
||||
|
||||
Person wendy = new Person("Wendy");
|
||||
# Static Members, Methods and Classes
|
||||
|
||||
steve.PrintInfo();
|
||||
<!-- headingDivider: 5 -->
|
||||
<!-- class: invert -->
|
||||
|
||||
## Static Members
|
||||
|
||||
### Non-static members
|
||||
|
||||
* So far, we have used *__non-static__* fields in our classes
|
||||
* Meaning that each instance of the class holds its own version of the field, and changing the value of it only affects that instance:
|
||||
```csharp
|
||||
class MyAwesomeClass
|
||||
{
|
||||
public int MyProperty { get; set; }
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
MyAwesomeClass instance1 = new MyAwesomeClass();
|
||||
MyAwesomeClass instance2 = new MyAwesomeClass();
|
||||
instance1.MyProperty = 100;
|
||||
instance2.MyProperty = 200; // instance1.MyProperty is still 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
wendy.PrintInfo();
|
||||
---
|
||||
|
||||
}
|
||||
* Likewise, non-static class methods **_have to_** be called through an instance:
|
||||
```csharp
|
||||
class MyAwesomeClass
|
||||
{
|
||||
public void PrintText(string text)
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
}
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
MyAwesomeClass instance = new MyAwesomeClass();
|
||||
instance.PrintText("Hello World"); // Outputs "Hello World"
|
||||
MyAwesomeClass.PrintText("Hello World"); // Results in an error
|
||||
}
|
||||
}
|
||||
```
|
||||
<div class='centered'>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
### Static members
|
||||
|
||||
* Let's declare a property `MyProperty` with the `static` keyword
|
||||
* It can be referenced *through the class*, but ***not*** through the instance:
|
||||
```csharp
|
||||
class MyAwesomeClass
|
||||
{
|
||||
public static int MyProperty { get; set; } = 100;
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
MyAwesomeClass instance = new MyAwesomeClass();
|
||||
Console.WriteLine(MyAwesomeClass.MyProperty); // Outputs "100"
|
||||
Console.WriteLine(instance.MyProperty); // Results in an error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<div class='centered'>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### Static members: An example
|
||||
|
||||
* In this example, a static field is used for keeping count on how many times the class has been instantiated:
|
||||
```csharp
|
||||
class Person
|
||||
{
|
||||
public static int totalPersons = 0;
|
||||
private string name;
|
||||
public Person(string personName) // Person Constructor
|
||||
{
|
||||
name = personName;
|
||||
++totalPersons;
|
||||
}
|
||||
public void PrintInfo()
|
||||
{
|
||||
Console.WriteLine("This person is called " + name + ".");
|
||||
Console.WriteLine("There are " + totalPersons + " persons total.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
}
|
||||
|
||||

|
||||
---
|
||||
|
||||
# Static Methods
|
||||
|
||||
Methods can also be static
|
||||
* Now let's instantiate a couple of persons and print their info:
|
||||
|
||||
What happens when you try to call a non-static method from a static method?
|
||||
<div class='columns21' markdown='1'>
|
||||
<div markdown='1'>
|
||||
|
||||
```csharp
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
void PrintHelloName(string name)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Hello, " + name);
|
||||
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
PrintHelloName(); // Will throw an error
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
This is why until this point all example methods have been inside of the main function
|
||||
|
||||
# Static Classes
|
||||
|
||||
* Classes can also be made static
|
||||
* Static classes cannot be instantiated
|
||||
* All members of a static class also have to be static
|
||||
* static class Styling
|
||||
* {
|
||||
* public static string fontFamily = "Verdana";
|
||||
* public static float fontSize = 12.5f;
|
||||
* }
|
||||
* class Program
|
||||
* {
|
||||
* static void Main(string[] args)
|
||||
* {
|
||||
* Console.WriteLine
|
||||
* ("Using font " + Styling.fontFamily + " " + Styling.fontSize + "px");
|
||||
* // Outputs "Using font Verdana 12.5px"
|
||||
* Styling = new Styling(); // Results in an error
|
||||
* }
|
||||
* }
|
||||
|
||||
# Exercise 1
|
||||
|
||||
Create a class Message which has two __static __ properties int TotalMessages and string LastMessage, and a __non-static __ property string MessageText.
|
||||
|
||||
Add a constructor which takes a string message as a parameter, increases TotalMessages by one and sets the value of LastMessage to message which is the parameter
|
||||
|
||||
Create a main loop which keeps asking the user for a new message. A new Message instance is then created with the user input message as an argument:
|
||||
|
||||
Message newMessage = new Message(message);
|
||||
|
||||
newMessage is then added to a list of messages, allMessages
|
||||
|
||||
Finally the static values Message.TotalMessages and Message.LastMessage are printed
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Person steve = new Person("Steve");
|
||||
Person wendy = new Person("Wendy");
|
||||
steve.PrintInfo();
|
||||
wendy.PrintInfo();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
<div markdown='1'>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Static methods
|
||||
|
||||
* Methods can also be static
|
||||
* What happens when you try to call a non-static method from a static method?
|
||||
```csharp
|
||||
class Program
|
||||
{
|
||||
void PrintHelloName(string name)
|
||||
{
|
||||
Console.WriteLine("Hello, " + name);
|
||||
}
|
||||
static void Main(string[] args)
|
||||
{
|
||||
PrintHelloName(); // Will throw an error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<div class='centered'>
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## Static classes
|
||||
|
||||
* Whole classes can also be static
|
||||
* Static classes cannot be instantiated, and all its members have to be static as well
|
||||
```csharp
|
||||
static class Styling
|
||||
{
|
||||
public static string fontFamily = "Verdana";
|
||||
public static float fontSize = 12.5f;
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine
|
||||
("Using font " + Styling.fontFamily + " " + Styling.fontSize + "px");
|
||||
// Outputs "Using font Verdana 12.5px"
|
||||
Styling = new Styling(); // Results in an error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Exercise 1
|
||||
<!--_class: "exercise invert" -->
|
||||
|
||||
* Create a class `Message` that has two `static` properties `int TotalMessages` and `string LastMessage`, and a *__non-static__* property `string MessageText`.
|
||||
* Add a constructor that takes a `string message` as a parameter, increases `TotalMessages` by one and sets the value of `LastMessage` to `message`.
|
||||
* Create a main loop that keeps asking the user for a new message. A new `Message` instance is then created with the user input message as an argument:
|
||||
```csharp
|
||||
Message newMessage = new Message(message);
|
||||
```
|
||||
* `newMessage` is then added to a list `allMessages`.
|
||||
* Finally, the static values `Message.TotalMessages` and `Message.LastMessage` are printed.
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,302 +1,307 @@
|
||||
# Inheritance & Abstract Classes
|
||||
|
||||

|
||||
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
math: mathjax
|
||||
theme: buutti
|
||||
title: 8. Inheritance & Abstract Classes
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Inheritance
|
||||
|
||||
Abstract Classes
|
||||
# Inheritance & Abstract Classes
|
||||
|
||||
Enums
|
||||
<!-- headingDivider: 5 -->
|
||||
<!-- class: invert -->
|
||||
|
||||
# Inheritance
|
||||
## Overview
|
||||
|
||||
Classes can be made to inherit functionality of some other class
|
||||
* Inheritance
|
||||
* Abstract classes
|
||||
* Enums
|
||||
* OOP
|
||||
|
||||
If class B inherits class A, all of the (public) functionality in class A is also available in class B
|
||||
## Inheritance
|
||||
|
||||
A is called the __base class__ (or parent class) and B is called the __derived class __ (or child class)
|
||||
* Classes can be made to inherit functionality of some other class
|
||||
* If class B inherits class A, all of the (public) functionality in class A is also available in class B
|
||||
* A is called the *__base class__* (or parent class) and B is called the *__derived class__* (or child class)
|
||||
* Use the `:` symbol to make a class inherit from another
|
||||
|
||||
Use the " __:"__ symbol to make a class inherit from another
|
||||
### An inheritance example
|
||||
|
||||
Try to understand the following example:
|
||||
<center>
|
||||
|
||||
| class Animal |
|
||||
| :-: |
|
||||
| -Eat()-Sleep() |
|
||||
| `class Animal` | `class Dog : Animal` | `class Cat : Animal` |
|
||||
|:--------------:|:--------------------:|:--------------------:|
|
||||
| `Eat()` | `Bark()` | `Meow()` |
|
||||
| `Sleep()` | | |
|
||||
|
||||
| class Dog : Animal |
|
||||
| :-: |
|
||||
| -Bark() |
|
||||
</center>
|
||||
|
||||
| class Cat : Animal |
|
||||
| :-: |
|
||||
| -Meow() |
|
||||
<br>
|
||||
|
||||
Animal can eat
|
||||
<div class='columns111' markdown='1'>
|
||||
<div markdown='1'>
|
||||
|
||||
Animal can sleep
|
||||
* Animal can eat
|
||||
* Animal can sleep
|
||||
|
||||
Dog can eat
|
||||
</div>
|
||||
<div markdown='1'>
|
||||
|
||||
Dog can sleep
|
||||
* Dog can eat
|
||||
* Dog can sleep
|
||||
* Dog can bark
|
||||
|
||||
Dog can bark
|
||||
</div>
|
||||
<div markdown='1'>
|
||||
|
||||
Cat can eat
|
||||
* Cat can eat
|
||||
* Cat can sleep
|
||||
* Cat can meow
|
||||
|
||||
Cat can sleep
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Cat can meow
|
||||
### Inheritance: An example implementation
|
||||
|
||||
# Inheritance - Example
|
||||
<div class='columns' markdown='1'>
|
||||
<div markdown='1'>
|
||||
|
||||
```csharp
|
||||
class Animal
|
||||
|
||||
{
|
||||
|
||||
public void Eat() { Console.WriteLine("Eating..."); }
|
||||
|
||||
public void Sleep() { Console.WriteLine("Sleeping..."); }
|
||||
|
||||
public void Eat()
|
||||
{ Console.WriteLine("Eating..."); }
|
||||
public void Sleep()
|
||||
{ Console.WriteLine("Sleeping..."); }
|
||||
}
|
||||
|
||||
class Cat : Animal
|
||||
|
||||
{
|
||||
|
||||
public void Meow() { Console.WriteLine("Meow!"); }
|
||||
|
||||
public void Meow()
|
||||
{ Console.WriteLine("Meow!"); }
|
||||
}
|
||||
|
||||
class Dog : Animal
|
||||
|
||||
{
|
||||
|
||||
public void Bark() { Console.WriteLine("Bark!"); }
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Dog pluto = new Dog();
|
||||
|
||||
pluto.Eat(); // Outputs "Eating..."
|
||||
|
||||
pluto.Bark(); // Outputs "Bark!"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Inheritance
|
||||
|
||||
All the objects deriving from the same base class can be referenced with the base class name:
|
||||
|
||||
Animal whiskersAnimal = new Cat();
|
||||
|
||||
Animal plutoAnimal = new Dog();
|
||||
|
||||
However, only the methods of the base class are available for objects of the base class type:
|
||||
|
||||
plutoAnimal.Eat(); // Outputs "Eating..."
|
||||
|
||||
plutoAnimal.Bark(); // Error
|
||||
|
||||
An object of base class type can be __casted __ into the child class type:
|
||||
|
||||
Dog pluto = (Dog)plutoAnimal;
|
||||
|
||||
pluto.Bark(); // Outputs "Bark!"
|
||||
|
||||
# Abstract Classes
|
||||
|
||||
* In some cases you want the base class to be made __abstract__
|
||||
* Objects of an abstract class cannot be instantiated
|
||||
* For example, where would you need a generic Animal by itself?
|
||||
* Make a class abstract with the abstract keyword:
|
||||
* abstract class Animal
|
||||
* {
|
||||
* public void Eat() { Console.WriteLine("Eating..."); }
|
||||
* public void Sleep() { Console.WriteLine("Sleeping..."); }
|
||||
* }
|
||||
* class Program
|
||||
* {
|
||||
* static void Main(string[] args)
|
||||
* {
|
||||
* Animal animal = new Animal(); // This will throw an error
|
||||
* }
|
||||
* }
|
||||
|
||||
Instead, the methods are accessible through a derived class:
|
||||
|
||||
abstract class Animal
|
||||
|
||||
{
|
||||
|
||||
public void Eat() { Console.WriteLine("Eating..."); }
|
||||
|
||||
public void Sleep() { Console.WriteLine("Sleeping..."); }
|
||||
|
||||
public void Bark()
|
||||
{ Console.WriteLine("Bark!"); }
|
||||
}
|
||||
```
|
||||
|
||||
class Pig : Animal
|
||||
|
||||
{
|
||||
|
||||
public void Squeal() { Console.WriteLine("Squeee!"); }
|
||||
|
||||
}
|
||||
</div>
|
||||
<div markdown='1'>
|
||||
|
||||
```csharp
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Pig pig = new Pig();
|
||||
|
||||
pig.Sleep(); // Outputs "Sleeping..."
|
||||
|
||||
pig.Squeal(); // Outputs "Squeee!"
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Dog pluto = new Dog();
|
||||
pluto.Eat(); // Outputs "Eating..."
|
||||
pluto.Bark(); // Outputs "Bark!"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
If you know you only need the functionality of the abstract class, instantiate the new class as a type of abstract class:
|
||||
|
||||
abstract class Animal{ public void Eat() { Console.WriteLine("Eating..."); } public void Sleep() { Console.WriteLine("Sleeping..."); }}class Pig : Animal{ public void Squeal() { Console.WriteLine("Squeee!"); }}class Program{ static void Main(string[] args) { Animal pig = new Pig(); pig.Sleep(); // Outputs "Sleeping..." pig.Squeal(); // This will throw an error }}
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
### Inheritance continued
|
||||
|
||||
* All the objects deriving from the same base class can be referenced with the base class name:
|
||||
```csharp
|
||||
Animal whiskersAnimal = new Cat();
|
||||
Animal plutoAnimal = new Dog();
|
||||
```
|
||||
* However, only the methods of the base class are available for objects of the base class type:
|
||||
```csharp
|
||||
plutoAnimal.Eat(); // Outputs "Eating..."
|
||||
plutoAnimal.Bark(); // Error
|
||||
```
|
||||
* An object of base class type can be *__cast__* into the child class type:
|
||||
```csharp
|
||||
Dog pluto = (Dog)plutoAnimal;
|
||||
pluto.Bark(); // Outputs "Bark!"
|
||||
```
|
||||
|
||||
## Abstract Classes
|
||||
|
||||
* In some cases you want the base class to be made [abstract](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/abstract)
|
||||
* Objects of an abstract class ***cannot*** be instantiated
|
||||
* For example, where would you need a generic `Animal` by itself?
|
||||
* Make a class abstract with the `abstract` keyword:
|
||||
```csharp
|
||||
abstract class Animal
|
||||
{
|
||||
public void Eat() { Console.WriteLine("Eating..."); }
|
||||
public void Sleep() { Console.WriteLine("Sleeping..."); }
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Animal animal = new Animal(); // This will throw an error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
This is called abstraction, which is one of the key concepts of OOP
|
||||
|
||||
# Exercise 1
|
||||
|
||||
Create the classes Animal, Dog, Cat and Pig. Animal is the same as before (strings name and sound and the Greet() method). Dog, Cat and Pig inherit Animal
|
||||
|
||||
Give Dog, Cat and Pig some fields and/or methods specific to that animal
|
||||
* Instead, the methods are accessible through a derived class:
|
||||
```csharp
|
||||
abstract class Animal
|
||||
{
|
||||
public void Eat() { Console.WriteLine("Eating..."); }
|
||||
public void Sleep() { Console.WriteLine("Sleeping..."); }
|
||||
}
|
||||
class Pig : Animal
|
||||
{
|
||||
public void Squeal() { Console.WriteLine("Squeee!"); }
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Pig pig = new Pig();
|
||||
pig.Sleep(); // Outputs "Sleeping..."
|
||||
pig.Squeal(); // Outputs "Squeee!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Create a few instances of Dog, Cat and Pig classes, and add them to a new list of Animals, named "allAnimals"
|
||||
|
||||
Loop through allAnimals with the foreach statement, and call the Greet() method of each animal
|
||||
|
||||
# Enum
|
||||
|
||||
__Enum __ (short for enumeration) is a data type for holding a set of constant (immutable) names
|
||||
|
||||
Enums can be useful when you have a number of items or states that are predefined, for example, weekdays
|
||||
|
||||
Create the enum type with the enum keyword:
|
||||
|
||||
enum Weekday{ Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}
|
||||
|
||||
New instances of the enum can only be assigned one of the values within:
|
||||
|
||||
Weekday currentDay = Weekday.Monday;
|
||||
|
||||
# Enum - Example
|
||||
|
||||
In this example, enum is used to keep track of the state of the program:
|
||||
---
|
||||
|
||||
* If you know you only need the functionality of the abstract class, instantiate the new class as a type of abstract class:
|
||||
```csharp
|
||||
abstract class Animal
|
||||
{
|
||||
public void Eat() { Console.WriteLine("Eating..."); }
|
||||
public void Sleep() { Console.WriteLine("Sleeping..."); }
|
||||
}
|
||||
class Pig : Animal
|
||||
{
|
||||
public void Squeal() { Console.WriteLine("Squeee!"); }
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Animal pig = new Pig();
|
||||
pig.Sleep(); // Outputs "Sleeping..."
|
||||
pig.Squeal(); // This will throw an error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Exercise 1. Animal farm
|
||||
<!--_class: "exercise invert" -->
|
||||
|
||||
1) Create the classes `Animal`, `Dog`, `Cat` and `Pig`. `Animal` is the same as before (strings `name` and `sound` and the `Greet()` method). `Dog`, `Cat` and `Pig` inherit `Animal`
|
||||
2) Give `Dog`, `Cat` and `Pig` some fields and/or methods specific to that animal
|
||||
3) Create a few instances of `Dog`, `Cat` and `Pig`, and add them to a list `allAnimals`
|
||||
4) Loop through `allAnimals` with the `foreach` statement, and call the `Greet()` method of each animal
|
||||
|
||||
## Enums
|
||||
|
||||
* [Enum](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/enum) (short for enumeration) is a data type for holding a set of constant (immutable) names
|
||||
* Enums can be useful when you have a given number of ***states*** that are predefined
|
||||
* For example, weekdays
|
||||
* Another example is an extension of boolean with three states `True`, `False` and `Maybe`
|
||||
|
||||
### Creating enums
|
||||
|
||||
* Create an `enum` type with the enum keyword:
|
||||
```csharp
|
||||
enum Season
|
||||
{
|
||||
Spring,
|
||||
Summer,
|
||||
Autumn,
|
||||
Winter
|
||||
}
|
||||
```
|
||||
* Then, create a new variable with the type `Season` and assign one of the enum type's values to it:
|
||||
```csharp
|
||||
Season currentSeason = Season.Spring;
|
||||
```
|
||||
|
||||
### Enums: An example
|
||||
|
||||
<div class='columns12' markdown='1'>
|
||||
<div markdown='1'>
|
||||
|
||||
In this example, an enum is used to keep track of the state of the program.
|
||||
|
||||
```csharp
|
||||
enum ProgramState
|
||||
|
||||
{
|
||||
|
||||
Login,
|
||||
|
||||
Menu,
|
||||
|
||||
Exit
|
||||
|
||||
Login,
|
||||
Menu,
|
||||
Exit
|
||||
}
|
||||
```
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
ProgramState currentState = ProgramState.Login;
|
||||
|
||||
while (true)
|
||||
|
||||
{
|
||||
|
||||
switch (currentState)
|
||||
</div>
|
||||
<div markdown='1'>
|
||||
|
||||
```csharp
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
case ProgramState.Login:
|
||||
|
||||
// Switch current state to Menu after logging in
|
||||
|
||||
break;
|
||||
|
||||
case ProgramState.Menu:
|
||||
|
||||
// Switch current state to Exit if user exits
|
||||
|
||||
break;
|
||||
|
||||
case ProgramState.Exit:
|
||||
|
||||
// Exit the program with an exit message
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ProgramState currentState = ProgramState.Login;
|
||||
while (true)
|
||||
{
|
||||
switch (currentState)
|
||||
{
|
||||
case ProgramState.Login:
|
||||
// Switch current state to Menu after logging in
|
||||
break;
|
||||
case ProgramState.Menu:
|
||||
// Switch current state to Exit if user exits
|
||||
break;
|
||||
case ProgramState.Exit:
|
||||
// Exit the program with an exit message
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
# Going Further: Object Oriented Programming
|
||||
## Extra: Object Oriented Programming
|
||||
<!-- _class: "extra invert" -->
|
||||
|
||||
* The instances that are created with the new -keyword are objects. This is literally what Object Orientation refers to: packing functionality into these reusable variables that are holding some data and can be passed around
|
||||
* The key concepts of OOP are
|
||||
* The instances that are created with the `new` keyword are ***objects***.
|
||||
* This is literally what ***object orientation*** refers to: packing functionality into these reusable variables that are holding some data and can be passed around
|
||||
* The key concepts of OOP are:
|
||||
* Encapsulation
|
||||
* Inheritance
|
||||
* Abstraction, and
|
||||
* Abstraction
|
||||
* Polymorphism
|
||||
|
||||
# OOP: Encapsulation
|
||||
|
||||
Earlier we created classes which hold properties and methods, which are only accessible elsewhere _after _ instantiating an object of the class
|
||||
|
||||
All the functionality is encapsulated inside of the class instead of lying around in the codebase
|
||||
|
||||
All the functionality made available _only when it is needed _ by instantiating an object of the class
|
||||
|
||||
# OOP: Inheritance
|
||||
|
||||
As shown in the lecture slides, inheritance allows you to write some functionality once, and then create separate classes which all share that same functionality
|
||||
|
||||
This removes the need to write the same code inside every class
|
||||
|
||||
# OOP: Abstraction
|
||||
### Encapsulation
|
||||
<!-- _class: "extra invert" -->
|
||||
|
||||
When your class contains a lot of complicated functionality, it doesn't always make sense to reveal everything when the class is used
|
||||
* Earlier we created classes that hold properties and methods that are only accessible elsewhere ***after*** instantiating an object of the class
|
||||
* All the functionality is encapsulated inside of the class instead of lying around in the codebase
|
||||
* All the functionality is made available **_only when it is needed_** by instantiating an object of the class
|
||||
|
||||
Instead, reveal only the parts that the user (you, a workmate, etc) actually need, with abstraction
|
||||
### Inheritance
|
||||
<!-- _class: "extra invert" -->
|
||||
|
||||
Parable: The user of a microwave doesn't have to know about the complicated circuitry inside of the microwave. Only the buttons are revealed
|
||||
* As shown earlier, inheritance allows you to write some functionality once, and then create separate classes which all share that same functionality
|
||||
* This removes the need to rewrite the same code inside every class
|
||||
|
||||
# OOP: Polymorphism
|
||||
### Abstraction
|
||||
<!-- _class: "extra invert" -->
|
||||
|
||||
This concept becomes more clear after we have covered interfaces
|
||||
* When your class contains a lot of complicated functionality, it doesn't always make sense to reveal everything when the class is used
|
||||
* Instead, reveal only the parts that the user (you, a workmate, etc) actually need, with abstraction
|
||||
* Example: The user of a microwave doesn't have to know about the complicated circuitry inside of the microwave. Only the buttons are revealed
|
||||
|
||||
Polymorphism refers to multiple classes having the same method but with different functionality
|
||||
### Polymorphism
|
||||
<!-- _class: "extra invert" -->
|
||||
|
||||
This reduces the need for massive if-else and switch..case statements
|
||||
* This concept becomes more clear after we have covered interfaces in the next lecture
|
||||
* Polymorphism refers to multiple classes having the same method but with different functionality
|
||||
* This reduces the need for massive if-else and switch..case statements
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,270 +1,248 @@
|
||||
# Interfaces
|
||||
|
||||

|
||||
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
math: mathjax
|
||||
theme: buutti
|
||||
title: 9. Interfaces
|
||||
---
|
||||
# Interfaces
|
||||
|
||||
# Overview
|
||||
<!-- headingDivider: 5 -->
|
||||
<!-- class: invert -->
|
||||
|
||||
Interfaces
|
||||
## Overview
|
||||
|
||||
Interfaces or Inheritance?
|
||||
* Interfaces
|
||||
* Interfaces or Inheritance?
|
||||
|
||||
# Interfaces
|
||||
## 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 that are defined in the interface
|
||||
* As with abstract classes, interfaces cannot be instantiated directly
|
||||
* It wouldn't make any sense as interfaces have no implementation
|
||||
* 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
|
||||
### Creating an interface
|
||||
|
||||
* Define an interface using the interface keyword instead of class:
|
||||
* interface IUser
|
||||
* {
|
||||
* int Id { get; set; }
|
||||
* string Name { get; set; }
|
||||
* void GetUserStatistics();
|
||||
* }
|
||||
* Interface names should begin with the capital I letter to more easily identify them as interfaces and not classes
|
||||
* Notice that interfaces can contain both properties and methods but not fields
|
||||
```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:
|
||||
|
||||
class User : IUser
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
### Implementing an interface
|
||||
|
||||
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
|
||||
* Implement an interface just like you would inherit a class:
|
||||
```csharp
|
||||
class User : IUser
|
||||
{
|
||||
|
||||
Let's fix that by defining those now
|
||||
}
|
||||
```
|
||||
* 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
|
||||
|
||||
# Implementing an Interface (continued)
|
||||
---
|
||||
|
||||
```csharp
|
||||
class User : IUser
|
||||
|
||||
{
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public void GetUserStatistics()
|
||||
|
||||
{
|
||||
|
||||
// Some code here
|
||||
|
||||
}
|
||||
|
||||
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_.
|
||||
|
||||
The interface is now fully implemented and the compiler is happy
|
||||
|
||||
The interface do not dictate _how_ the methods are implemented, those just need to be implemented.
|
||||
|
||||
❕
|
||||
|
||||
To quickly implement the interface, click the IUser interface name and click the light bulb -> implement interface.
|
||||
|
||||
# Implementing Multiple Interfaces
|
||||
### 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:
|
||||
* 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
|
||||
```csharp
|
||||
class Rock : IPickable, IThrowable, ICrushable
|
||||
{
|
||||
// Code that implements IPickable, IThrowable and ICrushable
|
||||
}
|
||||
```
|
||||
|
||||
In those cases, inheriting from some shared base class wouldn't make sense
|
||||
### Why use interfaces?
|
||||
|
||||
Consider the following example:
|
||||
|
||||
public void DeleteData(IDeletable data)
|
||||
|
||||
{
|
||||
|
||||
data.Delete();
|
||||
|
||||
}
|
||||
* 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
|
||||
|
||||
The above method accepts _any _ type of object that implements IDeletable, regardless of other functionality
|
||||
## Interface or Inheritance?
|
||||
|
||||
# Interface or Inheritance?
|
||||
### Example 1
|
||||
|
||||
| class Dog |
|
||||
| :-: |
|
||||
| -Eat()-Sleep()-WagTail() |
|
||||
<center>
|
||||
|
||||
| class Human |
|
||||
| :-: |
|
||||
| -Eat()-Sleep()-Contemplate() |
|
||||
| `class Dog` | `class Human` | `class Bear` |
|
||||
|:------------|:----------------|:--------------|
|
||||
| `Eat()` | `Eat()` | `Eat()` |
|
||||
| `Sleep()` | `Sleep()` | `Sleep()` |
|
||||
| `WagTail()` | `Contemplate()` | `Hibernate()` |
|
||||
|
||||
| class Bear |
|
||||
| :-: |
|
||||
| -Eat()-Sleep()-Hibernate() |
|
||||
</center>
|
||||
|
||||
All classes could inherit from a base class Animal, which has methods Eat() and Sleep()
|
||||
* 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()`?
|
||||
|
||||
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 |
|
||||
| :-: |
|
||||
| -Grow()-Photosynthesize() |
|
||||
<center>
|
||||
|
||||
| class Human |
|
||||
| :-: |
|
||||
| -Grow()-Move() |
|
||||
| `class Tree` | `class Human` | `class Car` |
|
||||
|:--------------------|:--------------|:------------|
|
||||
| `Grow()` | `Grow()` | `Explode()` |
|
||||
| `Photosynthesize()` | `Move()` | `Move()` |
|
||||
|
||||
| class Car |
|
||||
| :-: |
|
||||
| -Move()-Explode() |
|
||||
</center>
|
||||
|
||||
It wouldn't make sense to use inheritance here, since there is no functionality that is shared between all classes
|
||||
* 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`
|
||||
|
||||
Instead you could make two interfaces: IGrowable and IMovable
|
||||
## Interfaces: An example
|
||||
|
||||
# Interfaces - 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
|
||||
|
||||
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();
|
||||
|
||||
void Move();
|
||||
}
|
||||
|
||||
interface ICarryable
|
||||
|
||||
{
|
||||
|
||||
void Carry();
|
||||
|
||||
void Carry();
|
||||
}
|
||||
```
|
||||
|
||||
class Elephant : IMovable
|
||||
|
||||
{
|
||||
|
||||
public void Move()
|
||||
</div>
|
||||
<div markdown='1'>
|
||||
|
||||
```csharp
|
||||
class Elephant : IMovable
|
||||
{
|
||||
|
||||
Console.WriteLine("The elephant is moving'");
|
||||
|
||||
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");
|
||||
|
||||
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");
|
||||
|
||||
public void Carry()
|
||||
{
|
||||
Console.WriteLine("You are carrying the rock");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
---
|
||||
|
||||
static void Main(string[] args)
|
||||
<div class='columns21' markdown='1'>
|
||||
<div markdown='1'>
|
||||
|
||||
```csharp
|
||||
class Program
|
||||
{
|
||||
|
||||
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();
|
||||
|
||||
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'>
|
||||
|
||||

|
||||
|
||||
# Exercise 1: A Web of Relations
|
||||
|
||||
Create a console application that has an interface IInfo, and two classes Product and Category, which both implement IInfo.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Inside IInfo, declare a property InfoText and a method PrintInfo
|
||||
|
||||
Implement the property and method in Product and Category
|
||||
|
||||
Initialize a couple of products and categories, with info texts of your choice
|
||||
## Exercise 1: A web of relations
|
||||
<!--_class: "exercise invert" -->
|
||||
|
||||
Create a list of type IInfo, and add the products and categories to it
|
||||
Create a console application that has an interface `IInfo` and two classes `Product` and `Category` that both implement `IInfo`.
|
||||
|
||||
Create a main loop, where each time the user presses enter, all the info texts inside the list are printed
|
||||
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
|
||||
## 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.
|
||||
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.
|
||||
|
||||
Start by creating 4 classes: __Shape__ , __Square__ , __Triangle__ and __Circle__ . Square, Triangle and Circle inherit from Shape. Shape implements the __IComparable\<Shape>__ __ __ interface.
|
||||
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`.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
---
|
||||
<!--_class: "exercise invert" -->
|
||||
|
||||
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. Print the areas in the sorted array with a foreach loop. They should be printed in an increasing order.
|
||||
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.
|
||||
|
||||
|
Loading…
Reference in New Issue