@ -0,0 +1,165 @@
|
||||
# Introduction to C# and .NET
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Installing Visual Studio
|
||||
|
||||
A Brief History of .NET and C#
|
||||
|
||||
Creating Your First Application
|
||||
|
||||
Statements
|
||||
|
||||
Errors in Visual Studio 2022
|
||||
|
||||
Comments
|
||||
|
||||
# Installing Visual Studio
|
||||
|
||||
* Note: Visual Studio and Visual Studio Code are NOT the same thing
|
||||
* Visual Studio is a full-blown IDE (integrated development environment)
|
||||
* Visual Studio Code is more akin to text editors like Sublime Text, Atom etc.
|
||||
* While both can be used in certain situations, the C# and ASP.NET portions of this training assume that you are using Visual Studio
|
||||
* You can download the free version (Visual Studio Community) from [https://visualstudio.microsoft.com/vs/community/](https://visualstudio.microsoft.com/vs/community/)
|
||||
* This downloads the setup executable for VS, which will be used to download all the modules needed (see the next slide)
|
||||
|
||||
# Installing Visual Studio (cont.)
|
||||
|
||||
* After launching the setup executable, you will be prompted to select the components to be installed along with VS. For our purposes you will need "ASP.NET and web development" portion, found in the "Workloads" tab
|
||||
* Click "Install" found in the lower right corner to begin installing VS and the required components
|
||||
* Note: The installation can be quite large (8 GB or more) depending on which components are already installed on your system, so we recommend using a personal hotspot if possible
|
||||
* Multiple people downloading large amounts of data using the same public WIFI network will bog down the download speed for everyone
|
||||
* After installing VS, you can skip the account creation portion
|
||||
|
||||

|
||||
|
||||
# .NET
|
||||
|
||||
* .NET is Microsoft's software framework that turns your code (C#, F# or VB) into machine language
|
||||
* Two types of .NET Frameworks:
|
||||
* .NET Framework
|
||||
* 2001
|
||||
* Windows only
|
||||
* Comes pre-installed with every Windows OS
|
||||
* .NET Core
|
||||
* 2016
|
||||
* "Successor to .NET Framework"
|
||||
* Open source! [https://github.com/dotnet/](https://github.com/dotnet/) _core_
|
||||
* Cross-platform (Linux, Mac, Windows)
|
||||
* We will be using .NET Core during this course as the old .NET Framework is pretty much legacy
|
||||
|
||||
* Base Class Library (BCL)
|
||||
* A set of libraries that every .NET implementation uses
|
||||
* The libraries consist of common activities, like classes and value types, that you will use to create your programs
|
||||
* No need to reinvent the wheel with every new project
|
||||
|
||||
using System;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using System.Linq;
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
# C#
|
||||
|
||||
C# was developed for .NET Framework in 2000
|
||||
|
||||
Microsoft wanted a Windows exclusive competitor for Java
|
||||
|
||||
Easy to understand, general purpose, object oriented
|
||||
|
||||

|
||||
|
||||
Source: [http://pypl.github.io/PYPL.html](http://pypl.github.io/PYPL.html)
|
||||
|
||||
# Creating a Console Application
|
||||
|
||||
Open Visual Studio 2022.
|
||||
|
||||
Select "Create a new project"
|
||||
|
||||
Type "console" to the search bar and select the following template:
|
||||
|
||||

|
||||
|
||||
# Running a Console Application
|
||||
|
||||
To run your program, select "Debug" -> "Start Debugging" __or __ press F5 __or __ press the small play sign preceding your project name at the top middle
|
||||
|
||||
The template project should now run and print "Hello World!" to the console. Try changing the code so that it prints something else!
|
||||
|
||||
# C# Syntax - Statements
|
||||
|
||||
using System;
|
||||
|
||||
namespace MyAwesomeProgram
|
||||
|
||||
{
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Hello World!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rows = statements
|
||||
|
||||
Curly brackets ' _{' _ and ' _}' _ mark the start and the end of different _bodies _ of statements
|
||||
|
||||
Semicolon at the end of some statements
|
||||
|
||||
When to use semicolon? Don't worry: Visual Studio will tell you
|
||||
|
||||
# C# Syntax - Errors in Visual Studio 2022
|
||||
|
||||
Why is this throwing an error?
|
||||
|
||||
Missing one closing curly bracket
|
||||
|
||||

|
||||
|
||||
Hovering on the red underline will show a hint for the error:
|
||||
|
||||

|
||||
|
||||
All errors can be displayed by pressing the red cross in the bottom:
|
||||
|
||||

|
||||
|
||||
# C# Syntax - Comments
|
||||
|
||||
There are two basic ways to comment in C#:
|
||||
|
||||
// This is a comment
|
||||
|
||||
/* This is a
|
||||
|
||||
multi
|
||||
|
||||
line
|
||||
|
||||
comment */
|
||||
|
||||
Any text inside a comment will not be executed
|
||||
|
||||
Use comments to explain your code or comment out actual code for testing and debugging
|
||||
|
||||
# Assignments
|
||||
|
||||
[Assignments about this topic can be found here](https://gitea.buutti.com/education/academy-assignments/src/branch/master/C%23%20Basics/1.%20C%23%20Basics%20Assignments)
|
||||
|
@ -0,0 +1,49 @@
|
||||
# Introduction to Visual Studio
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
* An IDE (Integrated development environment) by Microsoft for Windows
|
||||
* Debug tools
|
||||
* Used for C#, C++, etc
|
||||
* Free Community edition
|
||||
* Multiple versions released
|
||||
* First version: 1997
|
||||
* 2017, 2019, 2022…
|
||||
|
||||
# Windows
|
||||
|
||||
_Solution Explorer_ (CTRL+ALT+L) shows the files in the project
|
||||
|
||||
_Terminal _ (CTRL+Ö) for command line stuff, like using Git
|
||||
|
||||
# Moving windows around
|
||||
|
||||

|
||||
|
||||
* Drag and hold windows to move them around
|
||||
* _Snap to layout_ window appears
|
||||
* After moving a window to the right, you can pin it to see its title on the right
|
||||
|
||||

|
||||
|
||||
# Keyboard shortcuts
|
||||
|
||||
* CTRL+.
|
||||
* If a type is missing, use this to add the needed using directive
|
||||
* CTRL+R, then CTRL+R (seriously?)
|
||||
* Rename variable
|
||||
* Press Enter to confirm
|
||||
* CTRL+C (without selecting), then CTRL+V
|
||||
* Duplicate line
|
||||
* ALT+Up, ALT+Down
|
||||
* Move line up/down
|
||||
|
||||
# Gotchas
|
||||
|
||||
* Some actions are unavailable when your app is running
|
||||
* Deleting files
|
||||
* Adding new controllers
|
@ -0,0 +1,233 @@
|
||||
# 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");
|
||||
|
||||
steve.PrintInfo();
|
||||
|
||||
wendy.PrintInfo();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# Static Methods
|
||||
|
||||
Methods can also be static
|
||||
|
||||
What happens when you try to call a non-static method from a static method?
|
||||
|
||||
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
|
||||
|
@ -0,0 +1,289 @@
|
||||
# Delegates and Events
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Delegates
|
||||
|
||||
Multicast Delegates
|
||||
|
||||
Anonymous Methods
|
||||
|
||||
Events
|
||||
|
||||
# Delegates
|
||||
|
||||
* __Delegates __ are reference type variables that hold a __reference to a method__ or multiple methods
|
||||
* Class objects hold a reference to a class instance, delegate objects hold a reference to a method / methods
|
||||
* Similar to function pointers in C and C++, or how any function in JavaScript works
|
||||
* Allows for methods to be passed as variables, useful for creating, for example, events
|
||||
|
||||
# Creating a Delegate
|
||||
|
||||
Declare a delegate using the following syntax:
|
||||
|
||||
delegate \<return type> \<delegate name>(\<parameters>);
|
||||
|
||||
Example:
|
||||
|
||||
delegate void PrintDelegate(string output);
|
||||
|
||||
This creates a new delegate of type void, named PrintDelegate and one parameter of type string
|
||||
|
||||
The referenced method return and parameter types have to match the delegate!
|
||||
|
||||
# Referencing a Delegate
|
||||
|
||||
After creating the delegate, it can be instantiated and the method assigned to it with the method name:
|
||||
|
||||
delegate void PrintDelegate(string output);
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
void PrintInLower(string text)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(text.ToLower());
|
||||
|
||||
}
|
||||
|
||||
PrintDelegate print = PrintInLower;
|
||||
|
||||
print("AaaBbbCcc"); // Outputs "aaabbbccc"
|
||||
|
||||
}
|
||||
|
||||
# Using Multicast Delegates
|
||||
|
||||
Delegates can be __composed of__ multiple methods using the "+" operator
|
||||
|
||||
Using the same PrintDelegate delegate as before, we could do this:
|
||||
|
||||
delegate void PrintDelegate(string output);
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
void PrintInLower(string text)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(text.ToLower());
|
||||
|
||||
}
|
||||
|
||||
void PrintInUpper(string text)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(text.ToUpper());
|
||||
|
||||
}
|
||||
|
||||
PrintDelegate print = PrintInLower;
|
||||
|
||||
print += PrintInUpper;
|
||||
|
||||
print("AaaBbbCcc");
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# Using Multicast Delegates (continued)
|
||||
|
||||
Methods can be removed from the delegate with the "-" operator:
|
||||
|
||||
delegate void PrintDelegate(string output);
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
...
|
||||
|
||||
print -= PrintInLower;
|
||||
|
||||
print("AaaBbbCcc"); // Outputs "AAABBBCCC"
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
|
||||
# Delegates - Example
|
||||
|
||||
Let's extend our previous example (without the "-=" part) by creating a new class called DelegateTest, and giving it a constructor that takes a PrintDelegate object as a parameter:
|
||||
|
||||
public class DelegateTest
|
||||
|
||||
{
|
||||
|
||||
public DelegateTest(PrintDelegate printDelegate)
|
||||
|
||||
{
|
||||
|
||||
printDelegate("This Method Was Called From Another Class!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Delegates - Example (continued)
|
||||
|
||||
Now we can create a new DelegateTest object and pass the delegate to the object constructor:
|
||||
|
||||
void PrintInLower(string text)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(text.ToLower());
|
||||
|
||||
}
|
||||
|
||||
void PrintInUpper(string text)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(text.ToUpper());
|
||||
|
||||
}
|
||||
|
||||
// Initialize new delegate which is composed of PrintInLower method
|
||||
|
||||
PrintDelegate print = PrintInLower;
|
||||
|
||||
// Add PrintInUpper method to the delegate
|
||||
|
||||
print += PrintInUpper;
|
||||
|
||||
// Send the delegate to the class constructor
|
||||
|
||||
DelegateTest delegateTest = new DelegateTest(print);
|
||||
|
||||

|
||||
|
||||
# Anonymous Methods
|
||||
|
||||
Delegates can be initialized anonymously (without a specified name)
|
||||
|
||||
Anonymous method in variable declaration:
|
||||
|
||||
delegate void PrintDelegate(string output);
|
||||
|
||||
bool printUpper = true;
|
||||
|
||||
PrintDelegate printCheckUpper =
|
||||
|
||||
delegate (string text)
|
||||
|
||||
{
|
||||
|
||||
if (printUpper)
|
||||
|
||||
Console.WriteLine(text.ToUpper());
|
||||
|
||||
else
|
||||
|
||||
Console.WriteLine(text);
|
||||
|
||||
};
|
||||
|
||||
printCheckUpper("I'm not angry!"); // Outputs I'M NOT ANGRY!
|
||||
|
||||
Notice that the actual method that prints the text is not declared anywhere
|
||||
|
||||
You can use an empty anonymous method to initialize a delegate which does nothing:
|
||||
|
||||
delegate void SomeDelegate();
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
// Initialize an empty delegate, add method later...
|
||||
|
||||
SomeDelegate myDelegate = new SomeDelegate(delegate { });
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Events
|
||||
|
||||
* Consider the following Game class:
|
||||
* class Game
|
||||
* {
|
||||
* Sound gameOverSound;
|
||||
* Window gameOverScreen;
|
||||
* // Some game logic here...
|
||||
* // ...
|
||||
* void OnGameOver()
|
||||
* {
|
||||
* gameOverSound.Play(); // This plays some sound
|
||||
* gameOverScreen.Show(); // This shows some screen
|
||||
* }
|
||||
* }
|
||||
* This raises a couple of problems:
|
||||
* The Game class has to know about the Sound and Window classes
|
||||
* Changes in either of the classes could break the code
|
||||
* = Game is dependent of Sound and Window classes
|
||||
---
|
||||
|
||||
Tight coupling
|
||||
|
||||
# Events (continued)
|
||||
|
||||
Events are signals that are raised by a __Publisher __ and received by a __Subscriber__
|
||||
|
||||
The publisher does not know or care what, if any, object receives the signal
|
||||
|
||||
Changes in the subscriber classes do not affect the publisher
|
||||
|
||||
# Events - Example
|
||||
|
||||
Events are created in two steps:
|
||||
|
||||
Declare a delegate
|
||||
|
||||
Declare a variable of the delegate with the event keyword
|
||||
|
||||
public delegate void GameOver();
|
||||
|
||||
class Game
|
||||
|
||||
{
|
||||
|
||||
public event GameOver GameOverEventHandler;
|
||||
|
||||
// Some game logic here...
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
|
||||
# Exercise 1: A Rudimentary Event System
|
||||
|
||||
Create a console application for controlling a plant treatment system, that has three methods which print the following outputs:
|
||||
|
||||
| void ReleaseWater() | Releasing water... |
|
||||
| :-: | :-: |
|
||||
| void ReleaseFertilizer() | Releasing fertilizer... |
|
||||
| void IncreaseTemperature() | Increasing temperature... |
|
||||
|
||||
Create a main loop where the user can switch each method on (= add to a delegate) by writing its name. All methods are off by default. (Hint: you can just use switch-case for defining which method should be added to the delegate)
|
||||
|
||||
If the user types run, all the methods that are on (= added to the delegate), will be executed.
|
||||
|
||||

|
||||
|
@ -0,0 +1,295 @@
|
||||
# Files and Streams
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Files and Streams
|
||||
|
||||
Reading from a File
|
||||
|
||||
Writing to a File
|
||||
|
||||
JSON Serialization
|
||||
|
||||
JSON Deserialization
|
||||
|
||||
# Files and Streams
|
||||
|
||||
The .NET library contains classes for manipulating files and directories
|
||||
|
||||
The FileStream class contains methods for read and write operations for files
|
||||
|
||||
The goal is usually to turn the data in your code into an outside resource, such as a .txt file, and vice versa
|
||||
|
||||
Application Code
|
||||
|
||||
Stream writer / reader
|
||||
|
||||
String, int, ...
|
||||
|
||||
FileStream object
|
||||
|
||||
.txt, .csv, .png, ...
|
||||
|
||||
---
|
||||
|
||||
https://learn.microsoft.com/en-us/dotnet/api/system.io?view=net-7.0
|
||||
|
||||
# Using the File Class
|
||||
|
||||
To get started, add the following namespace to your project:
|
||||
|
||||
using System.IO;
|
||||
|
||||
System.IO provides basic directory and folder support, and allows reading and writing to files and data streams
|
||||
|
||||
Check if a file exists with the .Exists() method:
|
||||
|
||||
string path = @"C:\\Users\\Public\\TestFolder\\TestFile.txt";
|
||||
|
||||
if (!File.Exists(path))
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine($"The file {path} does not exist.");
|
||||
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
https://learn.microsoft.com/en-us/dotnet/api/system.io?view=net-7.0
|
||||
|
||||
# Reading from a File
|
||||
|
||||
Get the entire document as a string with File.ReadAllText
|
||||
|
||||
Get all lines of a document as a string array with File.ReadAllLines
|
||||
|
||||
string path = @"C:\\Users\\Public\\TestFolder\\TestFile.txt";
|
||||
|
||||
if (!File.Exists(path))
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine($"The file {path} does not exist.");
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
var allLines = File.ReadAllLines(path);
|
||||
|
||||
foreach (string line in allLines)
|
||||
|
||||
Console.WriteLine(line);
|
||||
|
||||
}
|
||||
|
||||
# Writing to a File
|
||||
|
||||
Writing to a file is done with File.WriteAllText and File.WriteAllLines methods:
|
||||
|
||||
string path = @"C:\\Users\\Public\\TestFolder\\TestFile.txt";
|
||||
|
||||
string[] lyrics = new string[]
|
||||
|
||||
{
|
||||
|
||||
"Hello",
|
||||
|
||||
"Is it me you're looking for",
|
||||
|
||||
"I can see it in your eyes",
|
||||
|
||||
"I can see it in your smile"
|
||||
|
||||
};
|
||||
|
||||
if (File.Exists(path))
|
||||
|
||||
{
|
||||
|
||||
File.Delete(path); // If file exists, deletes the old file
|
||||
|
||||
}
|
||||
|
||||
File.WriteAllLines(path, lyrics); // Creates a new file with lyrics
|
||||
|
||||
# Exercise 1: A Simple Parser
|
||||
|
||||
Create a console application which keeps asking the user for a string. Each string is added into a list of strings.
|
||||
|
||||
If the user returns an empty string, the program asks for a path to a .txt file.
|
||||
|
||||
If the file exists, its contents are replaced with each string written to a separate line. If not, a new file with the contents is created.
|
||||
|
||||
# JSON Serialization
|
||||
|
||||
* JSON (JavaScript Object Notation) is a way to represent code objects in text form, a "data-interchange format"
|
||||
* Enables reading and writing series of objects into a text file
|
||||
* Consists of two types of structures
|
||||
* Objects, or unordered name-value pairs
|
||||
* Ordered lists of values
|
||||
* Language-independent, although the JSON notation resembles the C-family of languages (C#, Java, JavaScript etc.)
|
||||
---
|
||||
|
||||
https://www.json.org/json-en.html
|
||||
|
||||
# JSON Serialization (continued)
|
||||
|
||||
* .NET projects do not directly support JSON serialization
|
||||
* A __NuGet package__ for that exists, called __Newtonsoft.Json __
|
||||
* Install the package from _Tools _ -> _NuGet Package Manager_ -> M _anage NuGet Packages for Solution…_
|
||||
* Select _Browse_
|
||||
* The _Newtonsoft.Json_ should be the topmost package. If not, search for it from the search bar
|
||||
* Select the package, check the checkbox next to _Project _ and click _Install_
|
||||
|
||||
You can __serialize __ an object (e.g. a list) into a JSON string with the JsonConvert.SerializeObject() method:
|
||||
|
||||
class Product
|
||||
|
||||
{
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
List<Product> products = new List<Product>
|
||||
|
||||
{
|
||||
|
||||
new Product{Id=1, Name="Awesome Product"},
|
||||
|
||||
new Product{Id=2, Name="Terrible Product"}
|
||||
|
||||
};
|
||||
|
||||
string jsonString = JsonConvert.SerializeObject(products);
|
||||
|
||||
File.WriteAllText(@"C:\\Users\\OMISTAJA\\source\\repos\\Example\\Products.json", jsonString);
|
||||
|
||||
}
|
||||
|
||||
The contents of the output file Products.json are as follows:
|
||||
|
||||

|
||||
|
||||
# JSON Deserialization
|
||||
|
||||
__Deserializing __ the contents of a JSON file into an object in your program can be done with the JsonConvert.DeserializeObject() method:
|
||||
|
||||
string json = File.ReadAllText(
|
||||
|
||||
@"C:\\Users\\Johannes\\source\\repos\\Luentoesimerkit\\Luentoesimerkit\\db.json");
|
||||
|
||||
List<Product> deserializedProducts = new List<Product>();
|
||||
|
||||
deserializedProducts = JsonConvert.DeserializeObject<List<Product>>(json);
|
||||
|
||||
foreach(Product product in deserializedProducts)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(product.Name);
|
||||
|
||||
}
|
||||
|
||||
# Exercise 2: Serializing Your Classes
|
||||
|
||||
Create a class Note with the properties int Id, DateTime TimeStamp and string Text.
|
||||
|
||||
Create a main loop that keeps asking the user for a new note.
|
||||
|
||||
After the user has entered each note, a new Note object is created with a running Id (1, 2, 3…), TimeStamp set to current time and Text is the inputted note. Each new note is added to a list of notes, allNotes.
|
||||
|
||||
Every time a new note is submitted, the allNotes list is serialized into a json file, "notes.json". Set the path to the current project path (you can get the path by right-clicking the project in solution explorer and selecting "Copy Full Path". Remember to change the file name!)
|
||||
|
||||
# FileStream
|
||||
|
||||
The files have to be opened before read/write and closed after
|
||||
|
||||
Behind the scenes, the ReadAll... and WriteAll… methods automatically do the opening and closing, as described in the Microsoft documentation:
|
||||
|
||||

|
||||
|
||||
Writing to or reading only a part of the file requires opening and closing the file manually
|
||||
|
||||
This creates a __FileStream __ object that can be used for accessing parts of the file
|
||||
|
||||
# FileStream (continued)
|
||||
|
||||
When inserting string data into a file, it needs to be converted into bytes
|
||||
|
||||
string path = @"C:\\Users\\Public\\TestFolder\\TestWriteFile.txt";
|
||||
|
||||
int nOfLines = 4;
|
||||
|
||||
FileStream fs = null;
|
||||
|
||||
if (!File.Exists(path)) // If file does not exist
|
||||
|
||||
fs = File.Create(path); // create a new file and open it
|
||||
|
||||
else // If file exists
|
||||
|
||||
fs = File.OpenWrite(path); // open an existing file for writing
|
||||
|
||||
for (int i = 0; i < nOfLines; ++i)
|
||||
|
||||
{
|
||||
|
||||
string input = Console.ReadLine() + "\\n"; // Read user input, add newline
|
||||
|
||||
byte[] byteInput = new UTF8Encoding(true).GetBytes(input); // Get the string as a sequence of bytes
|
||||
|
||||
fs.Write(byteInput, 0, byteInput.Length); // Write the bytes at the end of the file
|
||||
|
||||
}
|
||||
|
||||
fs.Close(); // Close the file
|
||||
|
||||
# The Using Statement
|
||||
|
||||
From the Microsoft Documentation about the FileStream class:
|
||||
|
||||

|
||||
|
||||
This means that wrapping the file handling in an __using __ block handles the opening and closing of the stream for you
|
||||
|
||||
The following reads the first 20 characters of a file and prints them to the console
|
||||
|
||||
string path = @"C:\\Users\\Public\\TestFolder\\TestFile.txt";
|
||||
|
||||
int nOfBytesToPrint = 20;
|
||||
|
||||
byte[] byteArray = new byte[nOfBytesToPrint];
|
||||
|
||||
if (!File.Exists(path))
|
||||
|
||||
Console.WriteLine($"The file {path} does not exist.");
|
||||
|
||||
else
|
||||
|
||||
using (FileStream fs = File.OpenRead(path))
|
||||
|
||||
{
|
||||
|
||||
fs.Read(byteArray, 0, nOfBytesToPrint);
|
||||
|
||||
};
|
||||
|
||||
// No need to close the file since it was handled inside of a using block
|
||||
|
||||
Console.WriteLine(Encoding.UTF8.GetString(byteArray, 0, byteArray.Length));
|
||||
|
@ -0,0 +1,528 @@
|
||||
# Generics, IEnumerable and LINQ
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Generics
|
||||
|
||||
When using lists, you have to define the type of data that will be stored in the list, inside the angled brackets:
|
||||
|
||||
List<int> numberList = new List<int>(); // This list stores variables of type int
|
||||
|
||||
This means that list is a __generic__ class: it can contain data of any type
|
||||
|
||||
Classes, structs, interfaces and methods can also be generic
|
||||
|
||||
All the variables that are defined generic inside a generic container will be assigned a type only when the containing object/method is called
|
||||
|
||||
# Generics - Creating a Generic Class
|
||||
|
||||
class GenericClassExample<T>
|
||||
|
||||
{
|
||||
|
||||
public T value; // This value will be whatever type is specified at instantiation
|
||||
|
||||
public void PrintTypeAndValue()
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
($"This class contains a variable of type {value.GetType()} and of value {value}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
GenericClassExample<int> example = new GenericClassExample<int>();
|
||||
|
||||
example.value = 20;
|
||||
|
||||
example.PrintTypeAndValue();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# Generics - Multiple Type Parameters
|
||||
|
||||
Generic classes can receive multiple types as parameters
|
||||
|
||||
class CustomContainer<T1, T2, T3>
|
||||
|
||||
{
|
||||
|
||||
public T1 First { get; set; }
|
||||
|
||||
public T2 Second { get; set; }
|
||||
|
||||
public T3 Third { get; set; }
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
CustomContainer<int, string, DateTime> container
|
||||
|
||||
= new CustomContainer<int, string, DateTime>();
|
||||
|
||||
container.First = 10;
|
||||
|
||||
container.Second = "Testing.";
|
||||
|
||||
container.Third = DateTime.Now;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Generics - Creating a Generic Method
|
||||
|
||||
void GenericMethodExample<T>(T value)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
($"This method was passed a variable of type {value.GetType()} and of value {value}.");
|
||||
|
||||
}
|
||||
|
||||
GenericMethodExample<string>("ABC");
|
||||
|
||||

|
||||
|
||||
❕
|
||||
|
||||
You could name the generic type as anything, e.g. \<GenericType>. It is named \<T> by convention.
|
||||
|
||||
# Exercise 1: Initializing a Populated List
|
||||
|
||||
Create a generic method GetPopulatedList\<T> which takes two parameters: T value and int length, and returns a new list of type T which is populated with the value variables and has a length of length.
|
||||
|
||||
Test your method out with a couple of different types and lengths:
|
||||
|
||||
List<string> list = GetPopulatedList<string>("Hello, there", 10);
|
||||
|
||||
foreach(string value in list)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(value);
|
||||
|
||||
}
|
||||
|
||||
# IEnumerable
|
||||
|
||||
Lists and arrays are both __collections __ that implement the __IEnumerable __ interface
|
||||
|
||||
All objects that implement the IEnumerable interface can be iterated with the foreach statement
|
||||
|
||||
IEnumerable<string> names = new string[] {"Harry", "Luke", "Harley"};
|
||||
|
||||
IEnumerable<string> days = new List<string> {"Sunday", "Monday", "Friday"};
|
||||
|
||||
foreach (string name in names)
|
||||
|
||||
Console.WriteLine(name);
|
||||
|
||||
foreach (string day in days)
|
||||
|
||||
Console.WriteLine(day);
|
||||
|
||||
# IEnumerable (continued)
|
||||
|
||||
The IEnumerable interface itself doesn't hold much functionality
|
||||
|
||||

|
||||
|
||||
However, the LINQ library includes all the methods you would typically need to apply to IEnumerables, such as filtering
|
||||
|
||||
# LINQ
|
||||
|
||||
Some common query expressions occur repeatedly in code
|
||||
|
||||
E.g. trying to find an object with a certain id from an array:
|
||||
|
||||
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
||||
|
||||
{
|
||||
|
||||
new User { Id = 0, Name = "Rene" } ,
|
||||
|
||||
new User { Id = 1, Name = "Ville" }
|
||||
|
||||
};
|
||||
|
||||
User FindObjectWithId(int id)
|
||||
|
||||
{
|
||||
|
||||
foreach (User user in userArray)
|
||||
|
||||
if (user.Id == id)
|
||||
|
||||
return user;
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
Console.WriteLine(FindObjectWithId(1).Name); // Outputs "Ville"
|
||||
|
||||
# LINQ (continued)
|
||||
|
||||
* Having to write your own method for each possible query operation (select, filter, sort…) would of course be nonsensical
|
||||
* The List class includes some methods for manipulation, but...
|
||||
* These only work on lists; not all IEnumerables (e.g. arrays) contain those methods
|
||||
* Add to this all the different types of data (objects, SQL databases, XML, JSON…)
|
||||
|
||||
To introduce extensive query capabilities to all collection types, Language-Integrated Query (LINQ) was created
|
||||
|
||||
LINQ supports querying of objects and even XML and SQL data, directly in your code
|
||||
|
||||
Get started by adding the namespace to your project:
|
||||
|
||||
using System.Linq;
|
||||
|
||||
Here's the first example using LINQ:
|
||||
|
||||
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
||||
|
||||
{
|
||||
|
||||
new User { Id = 0, Name = "Rene" } ,
|
||||
|
||||
new User { Id = 1, Name = "Ville"}
|
||||
|
||||
};
|
||||
|
||||
Console.WriteLine(userArray.First(user => user.Id == 1).Name); // Outputs "Ville"
|
||||
|
||||
The __First __ -method returns the first result that satisfies the expression in the parameters
|
||||
|
||||
The arrow syntax above is called a __lambda expression__
|
||||
|
||||
# Lambda Expressions
|
||||
|
||||
Lambda expressions are a quick way of writing one line methods
|
||||
|
||||
The "First" -method of LINQ takes a __delegate __ as a parameter
|
||||
|
||||
The following code would do exactly the same thing as the example in the previous slide:
|
||||
|
||||
// Parameter references an existing method
|
||||
|
||||
bool GetUserWithId(User user)
|
||||
|
||||
{
|
||||
|
||||
return user.Id == 1;
|
||||
|
||||
}
|
||||
|
||||
Console.WriteLine(userList.First(GetUserWithId).Name); // Outputs "Ville"
|
||||
|
||||
As would the following:
|
||||
|
||||
// The delegate is created inside the parameters as an anonymous method
|
||||
|
||||
Console.WriteLine(userList.First(delegate (User user)
|
||||
|
||||
{ return user.Id == 1; })); // Outputs "Ville"
|
||||
|
||||
# Lambda Expressions (continued)
|
||||
|
||||
Using LINQs "First" -query with...
|
||||
|
||||
...a method:
|
||||
|
||||
bool GetUserWithId(User user)
|
||||
|
||||
{
|
||||
|
||||
return user.Id == 1;
|
||||
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
|
||||
userList.First(
|
||||
|
||||
GetUserWithId).Name);
|
||||
|
||||
// Outputs "Ville"
|
||||
|
||||
...an anonymous method:
|
||||
|
||||
Console.WriteLine(userList.First(
|
||||
|
||||
delegate (User user)
|
||||
|
||||
{
|
||||
|
||||
return user.Id == 1;
|
||||
|
||||
}));
|
||||
|
||||
// Outputs "Ville"
|
||||
|
||||
...a lambda expression:
|
||||
|
||||
Console.WriteLine(userList.First(
|
||||
|
||||
user =>
|
||||
|
||||
user.Id == 1).Name);
|
||||
|
||||
// Outputs "Ville"
|
||||
|
||||
# Lambda Expressions - Example
|
||||
|
||||
Normal methods can also be declared using the arrow function
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
// Method body assigned with lambda expression
|
||||
|
||||
string PrintCheckUpper(bool upper, string text) => upper ? text.ToUpper() : text;
|
||||
|
||||
PrintCheckUpper(true, "I'm not angry!"); // Outputs I'M NOT ANGRY!
|
||||
|
||||
}
|
||||
|
||||
# LINQ and Lambda Expressions
|
||||
|
||||
Going back to our LINQ example, it could be written without lambda expressions:
|
||||
|
||||
bool GetUserWithId(User user)
|
||||
|
||||
{
|
||||
|
||||
return user.Id == 1;
|
||||
|
||||
}
|
||||
|
||||
IEnumerable<User> userArray = new User[2] // Initialize a new array of users
|
||||
|
||||
{
|
||||
|
||||
new User { Id = 0, Name = "Rene" } ,
|
||||
|
||||
new User { Id = 1, Name = "Ville"}
|
||||
|
||||
};
|
||||
|
||||
Console.WriteLine(userArray.First(GetUserWithId).Name); // Outputs "Ville"
|
||||
|
||||
Here's the shorter version with lambda expression again:
|
||||
|
||||
Console.WriteLine(userArray.First(user => user.Id == 1).Name); // Outputs "Ville"
|
||||
|
||||
# LINQ Methods
|
||||
|
||||
LINQ contains methods for filtering, ordering, grouping, joining and selecting
|
||||
|
||||
Suppose we have a class Person that contains a property Country
|
||||
|
||||
The following performs filtering to persons object, returning only the persons whose country is of value "Finland":
|
||||
|
||||
List<Person> persons = new List<Person> {/* Insert data here */};
|
||||
|
||||
var queryResult = persons
|
||||
|
||||
.Where(person => person.Country == "Finland");
|
||||
|
||||
# LINQ Methods (continued)
|
||||
|
||||
| Method | Example | Description |
|
||||
| :-: | :-: | :-: |
|
||||
| Where | persons.Where(p => p.Country == "Finland") | Filters results based on an expression |
|
||||
| OrderBy | persons.OrderBy(p => p.LastName) | Orders results based on one of its properties |
|
||||
| Select | persons.Select(p => $"Dr. {p.LastName}") | Converts the enumerable into an another type based on an expression |
|
||||
| Skip | persons.Skip(1) | Skips first N elements |
|
||||
| Take | persons.Take(5) | Returns N elements |
|
||||
| ToList | persons.ToList() | Converts IEnumerable to a list |
|
||||
| ToArray | persons.ToArray() | Converts IEnumerable to an array |
|
||||
|
||||
---
|
||||
|
||||
Tässä on kaikki aritmeettiset operaattorit
|
||||
|
||||
| Method | Example | Description |
|
||||
| :-: | :-: | :-: |
|
||||
| Any | persons.Any(p => p.Country == "Finland") | Return true if at least one element satisfies a condition |
|
||||
| All | persons.All(p => p.Age >= 18) | Return true if all elements satisfy a condition |
|
||||
| FirstOrDefault | persons.FirstOrDefault(p => !p.Active) | Returns the first element that satisfies a condition, or null if not found |
|
||||
| Count | persons.Count(p => p.FirstName == "Mauri") | Returns the count of elements that satisfy a condition, can be left blank to count all |
|
||||
|
||||
More LINQ methods can be found [here](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=net-7.0)
|
||||
|
||||
---
|
||||
|
||||
Tässä on kaikki aritmeettiset operaattorit
|
||||
|
||||
# LINQ - Example
|
||||
|
||||
Suppose we have a csv file authors.csv in our project directory
|
||||
|
||||
// using System.IO;
|
||||
|
||||
string path = @"C:\\some\\path\\authors.csv";
|
||||
|
||||
var authors = File.ReadAllLines(path)
|
||||
|
||||
.Skip(1) // Skip the first line which contains the column titles
|
||||
|
||||
.Select(line => // Using lambda expression, return the new Author objects
|
||||
|
||||
{
|
||||
|
||||
var columns = line.Split(',');
|
||||
|
||||
return new Author
|
||||
|
||||
{
|
||||
|
||||
Id = int.Parse(columns[0]),
|
||||
|
||||
Name = columns[1],
|
||||
|
||||
Description = columns[2]
|
||||
|
||||
};
|
||||
|
||||
})
|
||||
|
||||
.ToList(); // Turn the resulting IEnumerable into a list
|
||||
|
||||
authors is now an in-memory list containing all the authors from the original csv file. Note how methods can be chained!
|
||||
|
||||
# Query Syntax
|
||||
|
||||
An alternative way of using LINQ is with the __query syntax__
|
||||
|
||||
The following performs filtering to persons object:
|
||||
|
||||
var queryResult = from person in persons
|
||||
|
||||
where person.country == "Finland"
|
||||
|
||||
select person;
|
||||
|
||||
The __range variable __ person works like the range variable in a foreach loop in that it holds a copy of the current element in the persons variable
|
||||
|
||||
The where keyword specifies the condition for the filter
|
||||
|
||||
# LINQ Queries - Example
|
||||
|
||||
Suppose we have a class City that contains a property Description
|
||||
|
||||
The following would print the descriptions of all cities that have descriptions less than 100 characters long
|
||||
|
||||
IEnumerable Cities = new List<City> {/* Insert data here */};
|
||||
|
||||
var filteredResult = from city in Cities
|
||||
|
||||
where city.Description.Length < 100
|
||||
|
||||
select city;
|
||||
|
||||
foreach (City result in filteredResult)
|
||||
|
||||
Console.WriteLine(result.Description);
|
||||
|
||||
# Extension Methods vs Query Syntax
|
||||
|
||||
* In the previous examples, we used the query syntax of LINQ
|
||||
* Both the methods and queries do pretty much the same thing
|
||||
* It is up to you which syntax you want to use
|
||||
* The method syntax works like any normal C# methods
|
||||
* The query syntax might be more approachable to those who are familiar with SQL
|
||||
|
||||
var methodResult = persons
|
||||
|
||||
.Where(person => person.Country == "Finland");
|
||||
|
||||
var queryResult = from person in persons
|
||||
|
||||
where person.Country == "Finland"
|
||||
|
||||
select person;
|
||||
|
||||
All queries listed here: [https://www.tutorialsteacher.com/linq/linq-standard-query-operators](https://www.tutorialsteacher.com/linq/linq-standard-query-operators)
|
||||
|
||||
# ToArray() and ToList() Methods
|
||||
|
||||
Notice that the LINQ queries return an IEnumerable
|
||||
|
||||
If you need to use arrays or lists, you need to call the ToArray() or ToList() methods:
|
||||
|
||||
string[] strings = new string[] { "Timo", "Pekka", "Taina", "Kalle" };
|
||||
|
||||
string[] queryResult = strings
|
||||
|
||||
.Where(s => s.StartsWith('T'));
|
||||
|
||||

|
||||
|
||||
string[] strings = new string[] { "Timo", "Pekka", "Taina", "Kalle" };
|
||||
|
||||
string[] queryResult = strings
|
||||
|
||||
.Where(s => s.StartsWith('T'))
|
||||
|
||||
.ToArray(); // This works
|
||||
|
||||
# Exercise 2: Filtering Names
|
||||
|
||||
Download this file of names and add it to your project folder: [https://raw.githubusercontent.com/dominictarr/random-name/master/names.txt](https://raw.githubusercontent.com/dominictarr/random-name/master/names.txt)
|
||||
|
||||
Read all the contents into a string array with File.ReadAllLines()
|
||||
|
||||
Create a main loop where the user is asked for a string. Print the total number of names which contain that string.
|
||||
|
||||
If there are less than 10 resulting names, print the names as well
|
||||
|
||||
# Exercise 3: Queries on Object Lists
|
||||
|
||||
Expand on the exercise 2.
|
||||
|
||||
Create a new class User with two properties, int Id and string Name
|
||||
|
||||
If the number of filtered names is less than 10, create a list of Users with those names and a running Id
|
||||
|
||||
Sort the list of users by the length of the Name property
|
||||
|
||||
Print the names and id:s of the users in the sorted list
|
||||
|
||||
# Going Further: Extension Methods
|
||||
|
||||
Recap: IEnumerable itself only contains one method
|
||||
|
||||
How does the LINQ library suddenly add all these methods to our Enumerables?
|
||||
|
||||
This is possible with extension methods:
|
||||
|
||||
[https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods)
|
||||
|
@ -0,0 +1,318 @@
|
||||
# Exceptions,
|
||||
Threads and Tasks
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Exceptions
|
||||
|
||||
* Exceptions are problems that occur during the lifetime of the program
|
||||
* Typical exceptions are IndexOutOfRangeException and NullReferenceException
|
||||
* IndexOutOfRangeException occurs when trying to access an element of an object using an index greater than the number of elements in that object
|
||||
* NullReferenceException occurs when trying to reference a variable that has not been assigned a value
|
||||
|
||||
# Exceptions (continued)
|
||||
|
||||
* There should be some kind of system to check for these problems so that even if they do occur, the application does not crash
|
||||
* For example, if a user tries to read from a file that does not exist, the user should be given an informative error message instead of closing the application
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
# try - catch
|
||||
|
||||
Wrap the code that could result in an exception in a __try __ block
|
||||
|
||||
Wrap the code that should execute if an exception occurs in a __catch __ block
|
||||
|
||||
string path = @"C:\\Users\\Public\\TestFolder\\WriteText.txt";
|
||||
|
||||
try
|
||||
|
||||
{
|
||||
|
||||
string text = System.IO.File.ReadAllText(path);
|
||||
|
||||
}
|
||||
|
||||
catch(DirectoryNotFoundException e)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine($"The path {path} does not exist.");
|
||||
|
||||
}
|
||||
|
||||
# throw
|
||||
|
||||
__throw __ - keyword results in the exception that is caught in the catch block
|
||||
|
||||
This can be used if you want to execute some code before stopping the execution of the program
|
||||
|
||||
string path = @"C:\\Users\\Public\\TestFolder\\WriteText.txt";
|
||||
|
||||
try
|
||||
|
||||
{
|
||||
|
||||
string text = System.IO.File.ReadAllText(path);
|
||||
|
||||
}
|
||||
|
||||
catch (IOException e)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("An exception occurred.");
|
||||
|
||||
Console.WriteLine("Logging exception information...");
|
||||
|
||||
Console.WriteLine($"Exception: {e.Message}");
|
||||
|
||||
throw;
|
||||
|
||||
}
|
||||
|
||||
# finally
|
||||
|
||||
__finally __ block executes after try-catch regardless of whether an exception occurred or not
|
||||
|
||||
This is useful for closing unmanaged objects like database connections and file streams
|
||||
|
||||
try
|
||||
|
||||
{
|
||||
|
||||
// Open a database connection or file stream
|
||||
|
||||
}
|
||||
|
||||
catch (SystemException e)
|
||||
|
||||
{
|
||||
|
||||
// Do this if connection / stream fails
|
||||
|
||||
}
|
||||
|
||||
finally
|
||||
|
||||
{
|
||||
|
||||
// Release the unmanaged resources from memory
|
||||
|
||||
}
|
||||
|
||||
# Exercise 1: Debugging Exceptions
|
||||
|
||||
Create a savings calculator which asks the user for a target amount of money the user is aiming for (in integers), and the users monthly salary. For parsing, use the int.Parse() method instead of int.TryParse().
|
||||
|
||||
The program then calculates the number of months it will take the user to reach the goal.
|
||||
|
||||
After displaying the result, the user is then asked for a path to a _folder _ where the result is then saved as _months\_until\_target.txt._
|
||||
|
||||
Try your program with different user inputs. Try to intentionally break your program and find as many exceptions as you can.
|
||||
|
||||
Handle the exceptions with exception handling, and inform the user of each exception with an appropriate message.
|
||||
|
||||
# Threads
|
||||
|
||||
* You might have time consuming functionality in your program, like
|
||||
* heavy algorithms and mathematical calculations, or
|
||||
* reading a large amount of data from a file
|
||||
* You could also have some continuous stream of data being read, like
|
||||
* serial signal
|
||||
* In case of time consuming methods, it might be a bad idea to wait for the execution to finish before continuing the main program
|
||||
* This would show up to the user as the program being stuck
|
||||
* At the very least, there could be some progress bar to indicate that the program hasn't crashed
|
||||
* In the case of continuous execution, there needs to be some way to read the stream and execute the main program at the same time
|
||||
|
||||
# Threads (continued)
|
||||
|
||||
.NET supports __multithreading __ and classes to add new threads are included in the base class library
|
||||
|
||||
using System.Threading;
|
||||
|
||||
Separate tasks can be executed simultaneously on separate __threads__
|
||||
|
||||
Separate threads run on different cores in the processor
|
||||
|
||||
All threads have access to the resources allocated to your program
|
||||
|
||||
Initialize a ThreadStart object with the method to be threaded as the argument:
|
||||
|
||||
public static void PrintInteger()
|
||||
|
||||
...
|
||||
|
||||
ThreadStart printIntegerStart = new ThreadStart(PrintInteger);
|
||||
|
||||
Initialize a Thread object with the ThreadStart object set as the argument, and start the new thread with Thread.Start() method:
|
||||
|
||||
Thread printIntegerThread = new Thread(printIntegerStart);
|
||||
|
||||
printIntegerThread.Start();
|
||||
|
||||
# Threads - Example
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
public static void PrintInteger()
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Thread started.");
|
||||
|
||||
int val = 0;
|
||||
|
||||
while (true)
|
||||
|
||||
{
|
||||
|
||||
Thread.Sleep(100);
|
||||
|
||||
Console.WriteLine("Current value: " + val);
|
||||
|
||||
++val;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
ThreadStart printIntegerStart = new ThreadStart(PrintInteger);
|
||||
|
||||
Console.WriteLine("Starting thread...");
|
||||
|
||||
Thread printIntegerThread = new Thread(printIntegerStart);
|
||||
|
||||
printIntegerThread.Start();
|
||||
|
||||
Console.WriteLine("Main thread continuing.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
The PrintInteger method prints a value, waits for 0.1sec, increases the value, prints it again and so on
|
||||
|
||||
The method keeps executing indefinitely, but the main thread continues execution as well
|
||||
|
||||

|
||||
|
||||
# Threads (continued)
|
||||
|
||||
You can create as many threads as you like:
|
||||
|
||||
ThreadStart printIntegerStart = new ThreadStart(PrintInteger);
|
||||
|
||||
Console.WriteLine("Starting thread...");
|
||||
|
||||
Thread printIntegerThread = new Thread(printIntegerStart);
|
||||
|
||||
printIntegerThread.Start();
|
||||
|
||||
Thread printIntegerThread2 = new Thread(printIntegerStart);
|
||||
|
||||
printIntegerThread2.Start();
|
||||
|
||||
Thread printIntegerThread3 = new Thread(printIntegerStart);
|
||||
|
||||
printIntegerThread3.Start();
|
||||
|
||||
# Exercise 2: Threaded Loading
|
||||
|
||||
Create a method public static void LoadData() which simulates loading resources by printing progress from 0% to 100%. Increase the progress in a loop with 1% increments. Use the Thread.Sleep() method to wait 10 ms between each increment.
|
||||
|
||||
Create a main loop where each time the user presses enter, the LoadData() method is executed on a separate thread.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Näytä ekstrana, miten tämä tehdään käyttäen luokan metodia eri luokan arvoilla
|
||||
|
||||
# Tasks
|
||||
|
||||
* In some cases you want to continue working on some tasks while others are queued to be executed
|
||||
* For example, after sending a server request, you might want to begin some tasks immediately and others only after a response has been received
|
||||
* Following [this great analogy](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/) from Microsoft docs, a cook doesn't have to wait for the eggs to be ready to begin brewing coffee or cooking bacon; instead, the cook would begin all tasks simultaneously
|
||||
* Threading won't help here; even if there was one cook (thread) for each task, they would all have to wait for the oven to heat up. One cook can just well do all the tasks (cooking) alone.
|
||||
|
||||
# Tasks - Continued
|
||||
|
||||
To create an asynchronous method, assign it with the async keyword. It is good practice to end the method name with Async
|
||||
|
||||
The method should return a value of the type that is specified in the angled brackets (if your task doesn't return a value, leave the brackets out)
|
||||
|
||||
Inside the method, call an asynchronous method with the await keyword:
|
||||
|
||||
static async Task<int> MyMethodAsync()
|
||||
|
||||
{
|
||||
|
||||
byte[] content = await client.GetByteArrayAsync("https://docs.microsoft.com/en-us/");
|
||||
|
||||
return content.Length;
|
||||
|
||||
}
|
||||
|
||||
This method can now be called asynchronously:
|
||||
|
||||
Task<int> myMethodResult = MyMethodAsync();
|
||||
|
||||
# Tasks - Example
|
||||
|
||||

|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static async Task<string> GetResultAsync()
|
||||
|
||||
{
|
||||
|
||||
int waitTime = 5000;
|
||||
|
||||
await Task.Delay(waitTime); // Replace this with your time consuming async functionality
|
||||
|
||||
return new string($"The task took {waitTime / 1000} seconds to finish.");
|
||||
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Starting operation...");
|
||||
|
||||
Task<string> resultTask = GetResultAsync();
|
||||
|
||||
Console.WriteLine("Operation has been started.");
|
||||
|
||||
Console.WriteLine("In the meantime, tell me something nice: ");
|
||||
|
||||
string somethingNice = Console.ReadLine();
|
||||
|
||||
resultTask.Wait(); // Wait for the task to complete
|
||||
|
||||
Console.WriteLine(resultTask.Result);
|
||||
|
||||
Console.ReadKey();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Exercise 3: Asynchronous Loading
|
||||
|
||||
Re-create the exercise 2, but instead of using separate threads for "loading the data", use an asynchronous method LoadDataAsync()
|
||||
|
@ -0,0 +1,133 @@
|
||||
# Design Patterns in C#
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
The Singleton Pattern
|
||||
|
||||
Dependency Injection
|
||||
|
||||
# The Singleton Pattern
|
||||
|
||||
* In some cases, it would make no sense to create an instance of a class every time it's members need to be accessed
|
||||
* For example, a shared resource manager that is being called from multiple classes
|
||||
* While a static class could be used for this, there are some problems:
|
||||
* As stated in the previous slide, static classes can only have static members
|
||||
* Static classes cannot be instantiated, so a reference to them cannot be passed around as a parameter
|
||||
* Static classes cannot inherit from other classes or implement interfaces
|
||||
* [And many more... ](https://www.c-sharpcorner.com/UploadFile/akkiraju/singleton-vs-static-classes/)
|
||||
|
||||
# The Singleton Pattern (continued)
|
||||
|
||||
* The __singleton __ class is a class that benefits from all the perks of a non-static class (non-static members, inheritance, referencing…), but only one (or zero) instances of it ever exists during the lifetime of your application
|
||||
* For example, reading from / writing to a file that should be accessible to multiple clients, should be made into a singleton
|
||||
* Instead of every client directly accessing the same file (and possibly causing massive performance issues), the singleton is instantiated once and a reference to it is provided to clients
|
||||
* The singleton could take care of queueing the read/write requests and be the only entity accessing the actual file
|
||||
|
||||
A singleton implementation could look something like this:
|
||||
|
||||
class Singleton
|
||||
|
||||
{
|
||||
|
||||
private static Singleton instance = null;
|
||||
|
||||
private Singleton() { }
|
||||
|
||||
public void MySingletonFunction()
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
("This function is accessible from everywhere!");
|
||||
|
||||
}
|
||||
|
||||
public static Singleton Instance
|
||||
|
||||
{
|
||||
|
||||
get
|
||||
|
||||
{
|
||||
|
||||
if (instance == null)
|
||||
|
||||
instance = new Singleton();
|
||||
|
||||
return instance;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Singleton.Instance.MySingletonFunction();
|
||||
|
||||
// Outputs "This function is accessible from everywhere!"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
The exact implementation of the singleton is out of the scope of this course, but it is important to understand that it exists and what its purpose is
|
||||
|
||||
Multitude of examples for different use cases are available and can be found by googling
|
||||
|
||||
# Dependency Injection
|
||||
|
||||
* Traditionally, when new objects of classes are instantiated, the consuming class handles the creation of the objects
|
||||
* A lot of classes change their functionality throughout the development of any project
|
||||
* This means that also _every single_ consuming class has to change
|
||||
* This is called __"tight coupling"__
|
||||
* What if, instead of directly creating the objects, they were provided by some interface that takes care of the creation?
|
||||
* This way, even if the base class changes, the consuming classes won't care because they only know about the provider
|
||||
* This provider is called __Container, __ and the functionality being injected is called __Service__
|
||||
* In ASP.NET, this container system is built in
|
||||
---
|
||||
|
||||
Show an example
|
||||
|
||||
Dependency injection in ASP.NET:
|
||||
|
||||
public class HomeController : Controller
|
||||
|
||||
{
|
||||
|
||||
private readonly IUserRepository \_userRepository;
|
||||
|
||||
public HomeController(IUserRepository userRepository)
|
||||
|
||||
{
|
||||
|
||||
\_userRepository = userRepository;
|
||||
|
||||
}
|
||||
|
||||
// User repository including all users is now accessible in HomeController
|
||||
|
||||
}
|
||||
|
||||
# Design Patterns
|
||||
|
||||
If the concepts of a singleton and dependency injection flew over your head, don't worry about it
|
||||
|
||||
The important thing is to know they exist so that when they come up again in ASP.NET, you have already familiarized yourself with the terms and understanding the logic behind ASP.NET becomes less overwhelming
|
||||
|
||||
There are many more design patterns, see the material [here](https://www.c-sharpcorner.com/UploadFile/bd5be5/design-patterns-in-net/)
|
||||
|
||||

|
||||
|
@ -0,0 +1,263 @@
|
||||
# Conditionals
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Conditionals
|
||||
|
||||
if and else
|
||||
|
||||
Logical Operators
|
||||
|
||||
# Conditionals
|
||||
|
||||
Less than: a < b
|
||||
|
||||
Less than or equal to: a <= b
|
||||
|
||||
Greater than: a > b
|
||||
|
||||
Greater than or equal to: a >= b
|
||||
|
||||
Equal to a == b
|
||||
|
||||
Not Equal to: a != b
|
||||
|
||||
Conditionals return true or false, meaning the result can even be allocated into a variable if needed
|
||||
|
||||
bool areEqual = (a == b);
|
||||
|
||||
# if , else and else if statements
|
||||
|
||||
double temperatureInOulu = 2.3;
|
||||
|
||||
double temperatureInIvalo = -10.9;
|
||||
|
||||
if (temperatureInOulu > temperatureInIvalo)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Oulu is warmer than Ivalo");
|
||||
|
||||
}
|
||||
|
||||
else if (temperatureInOulu < temperatureInIvalo)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Ivalo is warmer than Oulu");
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
("Oulu and Ivalo have the same temperature");
|
||||
|
||||
}
|
||||
|
||||
__if __ statements are executed in order
|
||||
|
||||
The first statement with a __true __ condition is executed
|
||||
|
||||
If no statement holds a true value, __else __ is executed
|
||||
|
||||
This outputs "Oulu is warmer than Ivalo"
|
||||
|
||||
# ! -Operator
|
||||
|
||||
Console.WriteLine(!true); // Outputs false
|
||||
|
||||
bool itsColdOutside = true;
|
||||
|
||||
if(!itsColdOutside)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("It's warm outside.");
|
||||
|
||||
}
|
||||
|
||||
The ! -operator flips the boolean value __ __
|
||||
|
||||
# The switch statement
|
||||
|
||||
The __switch __ statement compares the parameter value (here: the __path __ variable) with the value of each __case__
|
||||
|
||||
Only the matching code is executed
|
||||
|
||||
If no match is found, the default code block is executed
|
||||
|
||||
This outputs "Here's the catalogue!"
|
||||
|
||||
break ends the case and exits the switch: the code will not compile if omitted
|
||||
|
||||
string path = "/browse";
|
||||
|
||||
switch (path)
|
||||
|
||||
{
|
||||
|
||||
case "/browse":
|
||||
|
||||
Console.WriteLine("Here's the catalogue!");
|
||||
|
||||
break;
|
||||
|
||||
case "/contact":
|
||||
|
||||
Console.WriteLine("Here's our contact info.");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
Console.WriteLine("No such path!");
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
# Scope of Variables
|
||||
|
||||
Variables declared inside of blocks are called __local variables__ ; they are only accessible inside of that block.
|
||||
|
||||
int a = 0;
|
||||
|
||||
if(a < 10)
|
||||
|
||||
{
|
||||
|
||||
// Variable 'b' is only accessible inside of this if block
|
||||
|
||||
int b = 1;
|
||||
|
||||
b = 2; // This works
|
||||
|
||||
}
|
||||
|
||||
b = 3; // This throws an error
|
||||
|
||||
# Assignment Operators
|
||||
|
||||
We have used the assignment operator ' __='__ for assigning values for variables:
|
||||
|
||||
int x;
|
||||
|
||||
x = 25;
|
||||
|
||||
__Notice __ the difference between '=' and the conditional '=='!
|
||||
|
||||
'=' is used for assigning values for variables, '== is used for comparing values
|
||||
|
||||
# Logical Operators
|
||||
|
||||
'&&', '||' and '!' mean the logical AND, OR and NOT operators
|
||||
|
||||
For example,
|
||||
|
||||
int a = 1;
|
||||
|
||||
int b = 3;
|
||||
|
||||
int c = 5;
|
||||
|
||||
Console.WriteLine(a < b && a < c);
|
||||
|
||||
outputs "True"
|
||||
|
||||
| Operator | Name | Example | Description |
|
||||
| :-: | :-: | :-: | :-: |
|
||||
| && | AND | a && b | Returns true if __both__ variables are true.b is not evaluated if a == false. |
|
||||
| || | OR | a || b | Returns true if __one or both__ variables are true. b is not evaluated if a == true. |
|
||||
| ! | NOT | !a | __Negates__ boolean value (true becomes false and vice versa) |
|
||||
| ^ | XOR | a ^ b | Exclusive OR: returns true if __only __ a == true __OR only__ b == true. |
|
||||
| & | Logical AND | a & b | Returns true if __both__ variables are true. Both variables are always evaluated. |
|
||||
| | | Logical OR | a | b | Returns true if __one or both__ variables are true.Both variables are always evaluated. |
|
||||
|
||||
---
|
||||
|
||||
Tässä on kaikki aritmeettiset operaattorit
|
||||
|
||||
# Operators - Example
|
||||
|
||||
This outputs "Strong password!"
|
||||
|
||||
int uppercaseLetters = 2;
|
||||
|
||||
uppercaseLetters += 4; // is now 6
|
||||
|
||||
int specialCharacters = 2;
|
||||
|
||||
specialCharacters *= 2; // is now 4
|
||||
|
||||
if (uppercaseLetters >= 6 && specialCharacters >= 2)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Strong password!");
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("Weak-ass password...");
|
||||
|
||||
}
|
||||
|
||||
# Exercise 1:
|
||||
|
||||
Create a console application which asks the user which weekday it is and assigns the answer to a string variable.
|
||||
|
||||
Using a switch-case, calculate the days remaining until next Monday.
|
||||
|
||||
If the result is more than 3, print "Have a nice week!". Otherwise, print "Have a nice weekend!".
|
||||
|
||||
# Exercise 2:
|
||||
|
||||
Create a console application which lets the user input a note.
|
||||
|
||||
If the length of the note is less than 30, the program prints the current time and the note, separated with a tab. Otherwise, the date and the note are printed to a separate line.
|
||||
|
||||
Tip: Use DateTime.Now.ToString() for current time. Use .Length after your message variable to get the length of the message.
|
||||
|
||||
# One More Thing...
|
||||
|
||||
If a code block following a statement only has one line of code, you can write the code without using curly brackets:
|
||||
|
||||
int baa = 49;
|
||||
|
||||
if (baa > 20)
|
||||
|
||||
Console.WriteLine("Baa");
|
||||
|
||||
else
|
||||
|
||||
Console.WriteLine("Not baa!");
|
||||
|
||||
if (baa > 20) Console.WriteLine("Baa");
|
||||
|
||||
else Console.WriteLine("Not baa!");
|
||||
|
||||
You may see code where this is done. However, we highly recommend you not to use this syntax as it is highly prone to [errors](https://www.leadingagile.com/2018/01/the-goto-fail-bug-as-a-coaching-tool/) .
|
||||
|
||||
# Get Help
|
||||
|
||||
All the basics covering the syntax in C# are covered here:
|
||||
|
||||
[https://www.tutorialspoint.com/csharp/index.](https://www.tutorialspoint.com/csharp/index.htm) [htm](https://www.tutorialspoint.com/csharp/index.htm) [l](https://www.tutorialspoint.com/csharp/index.htm)
|
||||
|
||||
[https://www.w3schools.com/cs/default.asp](https://www.w3schools.com/cs/default.asp)
|
||||
|
||||
# Assignments
|
||||
|
||||
[Assignments about this topic can be found here](https://gitea.buutti.com/education/academy-assignments/src/branch/master/C%23%20Basics/3.%20Conditionals)
|
||||
|
@ -0,0 +1,246 @@
|
||||
# Loops
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
While Loop
|
||||
|
||||
For Loop
|
||||
|
||||
Nested Loops
|
||||
|
||||
Breaking Out of Loops
|
||||
|
||||
# Loops
|
||||
|
||||
Loops are useful when you need to repeat a block of code multiple times and to avoid code like this:
|
||||
|
||||
Console.WriteLine(count);
|
||||
|
||||
++count;
|
||||
|
||||
Console.WriteLine(count);
|
||||
|
||||
++count;
|
||||
|
||||
Console.WriteLine(count);
|
||||
|
||||
// Yuck! Nobody wants to see this.
|
||||
|
||||
Loops can also be used for iterating through arrays and lists (more on those later)
|
||||
|
||||
# while Loop
|
||||
|
||||
_while_ loop keeps executing its code block as long as the condition in its statement is true:
|
||||
|
||||
int count = 0;
|
||||
|
||||
while(count < 4)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(count);
|
||||
|
||||
++count;
|
||||
|
||||
}
|
||||
|
||||
// Outputs '0', '1', '2' and '3'
|
||||
|
||||
# do..while Loop
|
||||
|
||||
_do..while_ works the same way as while, but it executes the code at least once and checks the condition at the end:
|
||||
|
||||
int count = 0;
|
||||
|
||||
do
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(count);
|
||||
|
||||
++count;
|
||||
|
||||
} while (count < 4);
|
||||
|
||||
// Outputs '0', '1', '2' and '3'
|
||||
|
||||
# for Loop
|
||||
|
||||
_for _ loops are used when the number of iterations are predefined
|
||||
|
||||
for loop executes in three inherent steps:
|
||||
|
||||
for (/*initial value*/;/*condition*/;/*increment*/)
|
||||
|
||||
{
|
||||
|
||||
/* Code to be executed */
|
||||
|
||||
}
|
||||
|
||||
The initial value is set before the first iteration. The condition is checked before each iteration, and the increment is executed after each iteration.
|
||||
|
||||
# for Loop (continued)
|
||||
|
||||
Try to understand the following two examples:
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
("Current: " + i);
|
||||
|
||||
}
|
||||
|
||||
/* Outputs
|
||||
|
||||
Current: 0
|
||||
|
||||
Current: 1
|
||||
|
||||
Current: 2
|
||||
|
||||
Current: 3
|
||||
|
||||
*/
|
||||
|
||||
for (int i = 8; i > 0; i -= 2)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
("Current: " + i);
|
||||
|
||||
}
|
||||
|
||||
/* Outputs
|
||||
|
||||
Current: 8
|
||||
|
||||
Current: 6
|
||||
|
||||
Current: 4
|
||||
|
||||
Current: 2
|
||||
|
||||
*/
|
||||
|
||||
We start from zero, print the current value and add one until the value is no longer under four.
|
||||
|
||||
We start from eight, print the current value and remove two until the value is no longer larger than zero.
|
||||
|
||||
for (int i = 8; i > 0; i -= 2)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
("Current: " + i);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
("Current: " + i);
|
||||
|
||||
}
|
||||
|
||||
# Nested Loops
|
||||
|
||||
Nothing prevents you from using a loop inside another loop:
|
||||
|
||||
int columns = 3;
|
||||
|
||||
int rows = 4;
|
||||
|
||||
for (int i = 0; i < columns; ++i)
|
||||
|
||||
{
|
||||
|
||||
for (int j = 0; j < rows; ++j)
|
||||
|
||||
{
|
||||
|
||||
// Console.Write doesn't add a line break after printout
|
||||
|
||||
Console.Write("(" + j.ToString() + "," + i.ToString() + ")");
|
||||
|
||||
}
|
||||
|
||||
Console.WriteLine("");
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# Breaking Out of Loops
|
||||
|
||||
To stop the execution of a loop completely, use the _break _ -keyword:
|
||||
|
||||
int i = 0;
|
||||
|
||||
while(true)
|
||||
|
||||
{
|
||||
|
||||
++i;
|
||||
|
||||
if(i > 3)
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// i is now 4
|
||||
|
||||
To skip the current iteration, use the _continue _ -keyword
|
||||
|
||||
int i = 0;
|
||||
|
||||
while(i < 10)
|
||||
|
||||
{
|
||||
|
||||
++i;
|
||||
|
||||
if (i % 2 == 0)
|
||||
|
||||
continue;
|
||||
|
||||
Console.WriteLine(i);
|
||||
|
||||
}
|
||||
|
||||
// Prints every odd number from 1 to 10
|
||||
|
||||
# Exercise 1: The Main Loop
|
||||
|
||||
At the heart of every dynamic program, there is something called the _event loop _ a.k.a the _main loop. _ Create a console application which keeps asking the user for an input, until the user inputs "quit".
|
||||
|
||||
Expand the program so that if the user inputs "help", a dialog shows up which shows the command and explanation for the command for both quit and help commands.
|
||||
|
||||

|
||||
|
||||
# Exercise 2: Prime Numbers
|
||||
|
||||
Prime number is a number greater than 1 that is not a product of two small numbers, i.e. cannot be divided into a whole number. (Wikipedia: [https://en.wikipedia.org/wiki/Prime\_number](https://en.wikipedia.org/wiki/Prime_number) )
|
||||
|
||||
Create a console application which prints all the prime numbers between 0 .. 40
|
||||
|
||||

|
||||
|
||||
# Assignments
|
||||
|
||||
[Assignments about this topic can be found here](https://gitea.buutti.com/education/academy-assignments/src/branch/master/C%23%20Basics/4.%20Loops)
|
||||
|
@ -0,0 +1,204 @@
|
||||
# Arrays and Lists
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Arrays
|
||||
|
||||
Multidimensional Arrays
|
||||
|
||||
Lists
|
||||
|
||||
Iterating a List
|
||||
|
||||
Foreach
|
||||
|
||||
# Arrays
|
||||
|
||||
Arrays are a collection of variables of the same type, which allocate neighboring memory locations
|
||||
|
||||
A single value in this collection is called an __element__
|
||||
|
||||
Arrays can be __declared __ with square brackets following the type of the elements:
|
||||
|
||||
int[] userIds;
|
||||
|
||||
Declaring an array does not yet allocate space from memory
|
||||
|
||||
# Arrays (continued)
|
||||
|
||||
__Initialize __ an array with a length of 3 using the following syntax:
|
||||
|
||||
int[] userIds = new int[3];
|
||||
|
||||
__Assign a value__ to an element in an array by specifying the index in square brackets:
|
||||
|
||||
userIds[0] = 104;
|
||||
|
||||
Indexing starts from 0, so the above line assigns a value of 104 to the first element of the array
|
||||
|
||||
You can also create an array containing values with one statement:
|
||||
|
||||
string[] names = new string[3] { "Johannes", "Rene", "Ville" };
|
||||
|
||||
The same works without specifying the length in the brackets:
|
||||
|
||||
double[] balances = new double[] { 1.3, 200.3, 9332.14 };
|
||||
|
||||
# Multidimensional Arrays
|
||||
|
||||
C# supports multidimensional arrays:
|
||||
|
||||
char[,] letters = new char[3, 4]
|
||||
|
||||
{
|
||||
|
||||
{'a', 'b', 'c', 'd'},
|
||||
|
||||
{'e', 'f', 'g', 'h'},
|
||||
|
||||
{'i', 'j', 'k', 'l'}
|
||||
|
||||
};
|
||||
|
||||
Console.WriteLine(letters[1, 3]); // Outputs "h"
|
||||
|
||||
# Lists
|
||||
|
||||
The .NET Framework Class Library offers another way to hold multiple variables: the _List _ object
|
||||
|
||||
Declaration and initialization:
|
||||
|
||||
List<int> userIds = new List<int>();
|
||||
|
||||
Value assignment:
|
||||
|
||||
userIds[0] = 22;
|
||||
|
||||
# Lists (continued)
|
||||
|
||||
The key benefit of using Lists lies in its built-in functionalities
|
||||
|
||||

|
||||
|
||||
You can see a list of available methods in VSC2019 by following a variable with a dot
|
||||
|
||||
# Arrays vs Lists
|
||||
|
||||
The memory allocation of an array is static/fixed, but list memory is dynamic
|
||||
|
||||
This allows the flexible mutation of lists, meaning you can always add or remove and hence change change the length of the list without issues
|
||||
|
||||

|
||||
|
||||
# Arrays vs Lists (continued)
|
||||
|
||||
When to use arrays and when lists?
|
||||
|
||||
Use __arrays __ if you need __high performance__
|
||||
|
||||
Use __lists __ if you need support for __list operations__
|
||||
|
||||
# Iterating an Array With for Loop - Example
|
||||
|
||||
string[] names = new string[]
|
||||
|
||||
{
|
||||
|
||||
"Harry Potter",
|
||||
|
||||
"Luke Skywalker",
|
||||
|
||||
"Harley Quinn"
|
||||
|
||||
};
|
||||
|
||||
for(int i = 0; i < names.Length; ++i)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(names[i]);
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# Iterating a List
|
||||
|
||||
You can iterate the elements of a list with a for loop the same way as an array
|
||||
|
||||
Just use List.Count instead of Array.Length
|
||||
|
||||
List<int> numbers = new List<int>() { 1, 5, 3 };
|
||||
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i < numbers.Count; ++i)
|
||||
|
||||
{
|
||||
|
||||
sum += numbers[i];
|
||||
|
||||
}
|
||||
|
||||
// sum is now 9
|
||||
|
||||
# foreach Statement
|
||||
|
||||
There is one more statement to use to iterate arrays and lists: the foreach statement
|
||||
|
||||
foreach is useful when you have to execute code for each element of an array or a list:
|
||||
|
||||
string[] names = new string[]
|
||||
|
||||
{
|
||||
|
||||
"Harry Potter",
|
||||
|
||||
"Luke Skywalker",
|
||||
|
||||
"Harley Quinn"
|
||||
|
||||
};
|
||||
|
||||
foreach (string name in names)
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine(name);
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# foreach Statement (continued)
|
||||
|
||||
However, foreach creates a copy of each element in the object so the element cannot be mutated directly:
|
||||
|
||||

|
||||
|
||||
Performance-wise, using foreach is also more costly because it uses more memory space
|
||||
|
||||
---
|
||||
|
||||
Instead, create a new list
|
||||
|
||||
# Exercise 1: Expanding the Console Application
|
||||
|
||||
Continue working on the command line application you created in "The Main Loop" exercise. Add a new command "add" which prompts the user to write a note.
|
||||
|
||||
After the user has inputted the note, it is saved to a list, and the program returns back to listening to commands.
|
||||
|
||||
Add another command "list" which prints all the saved notes.
|
||||
|
||||
Add one more command "remove" which prints all the saved notes with the index of the note, and then prompts the user for a number. After entering the number the corresponding note is deleted from the list.
|
||||
|
||||
( __Note__ : you can use int.Parse() -method to parse the user input string to an integer)
|
||||
|
||||
# Assignments
|
||||
|
||||
[Assignments about this topic can be found here](https://gitea.buutti.com/education/academy-assignments/src/branch/master/C%23%20Basics/5.%20Arrays%20&%20Lists)
|
||||
|
@ -0,0 +1,270 @@
|
||||
# Methods
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Functions
|
||||
|
||||
Methods
|
||||
|
||||
Parameters
|
||||
|
||||
Return Values
|
||||
|
||||
Scope of Methods
|
||||
|
||||
# What is a function?
|
||||
|
||||
* Functions are self contained modules of code that accomplish a specific task.
|
||||
* Function is basically a block of code that can be executed over and over again in the program
|
||||
* Instead of writing the same code over and over again to the program, it can be written only once as a function
|
||||
* Functions can get data, _parameters_ , from the _caller_
|
||||
* Functions can return data, _return value_ , to the caller
|
||||
* Defining parameters and return values are optional, not all functions have both and some have neither!
|
||||
|
||||
# Function execution
|
||||
|
||||
* When a function is called the program leaves the current section of the code and begins to execute the first line of code inside the function.
|
||||
* The program execution comes to the line of code where the function call is
|
||||
* The program enters the function
|
||||
* Instructions inside the function are executed from top to bottom
|
||||
* Remember scopes: The variables defined inside the function are not visible outside the function!
|
||||
* The program leaves the function and returns to the point where the function call was and continues the execution
|
||||
* If function returned any data, it can be used where the function was called
|
||||
|
||||
# Methods
|
||||
|
||||
In C#, __methods __ are functions which are inside of a class
|
||||
|
||||
Since the main function is inside of a class, all functions in C# are actually just called methods
|
||||
|
||||
* A methods are executed only when it is __called__
|
||||
* For example, Console.WriteLine() is a method which contains more complex code to print stuff on to the console
|
||||
* Instead of writing all that complex stuff at every printout, you can just use the short method call Console.WriteLine()
|
||||
* The use of methods reduces repetitive code, adds modularity and makes designing your programs easier
|
||||
|
||||
# Methods - Example
|
||||
|
||||

|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
void PrintBlock()
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine("---------------------");
|
||||
|
||||
Console.WriteLine("| This is cool but |");
|
||||
|
||||
Console.WriteLine("| you wouldn't want |");
|
||||
|
||||
Console.WriteLine("| to write this |");
|
||||
|
||||
Console.WriteLine("| block of code |");
|
||||
|
||||
Console.WriteLine("| every time! |");
|
||||
|
||||
Console.WriteLine("---------------------");
|
||||
|
||||
}
|
||||
|
||||
PrintBlock();
|
||||
|
||||
PrintBlock();
|
||||
|
||||
PrintBlock();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
* In this example, we declared a new method called PrintBlock
|
||||
* Finally the method was called three times
|
||||
* Each time the entire code inside the method was executed
|
||||
* The void statement means that the method does not return anything
|
||||
* More on that later
|
||||
|
||||
# Parameters
|
||||
|
||||
Sometimes you want to pass data into the method when calling it
|
||||
|
||||
This is done by adding __parameters __ to the method declaration, inside the brackets:
|
||||
|
||||
void PrintSum(int val1, int val2)
|
||||
|
||||
{
|
||||
|
||||
int sum = val1 + val2;
|
||||
|
||||
Console.WriteLine("Sum of " + val1 + " and " + val2 + " is " + sum);
|
||||
|
||||
}
|
||||
|
||||
The values to be passed are set as __arguments __ in the method call, inside the brackets:
|
||||
|
||||
PrintSum(2, 5); // Outputs "Sum of 2 and 5 is 7
|
||||
|
||||
PrintSum(5, 5); // Outputs "Sum of 5 and 5 is 10
|
||||
|
||||
# Parameters - Example
|
||||
|
||||

|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
int centuriesPrinted = 0;
|
||||
|
||||
void PrintCentury(int century)
|
||||
|
||||
{
|
||||
|
||||
centuriesPrinted++;
|
||||
|
||||
Console.WriteLine("Current century: " + century);
|
||||
|
||||
Console.WriteLine("Total centuries printed: " + centuriesPrinted);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < 500; i += 100)
|
||||
|
||||
{
|
||||
|
||||
PrintCentury(i);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Optional Parameters
|
||||
|
||||
You can give a parameter a default value by assigning it in the method declaration
|
||||
|
||||
This makes the parameter optional
|
||||
|
||||
double CircleArea(double radius = 0)
|
||||
|
||||
{
|
||||
|
||||
double area = Math.PI * radius * radius;
|
||||
|
||||
return area;
|
||||
|
||||
}
|
||||
|
||||
Console.WriteLine(CircleArea()); // This outputs 0
|
||||
|
||||
Console.WriteLine(CircleArea(2)); // This outputs 12,56637...
|
||||
|
||||
# Exercise 1
|
||||
|
||||
Write two methods Sum and Difference, which both have two parameters of type int, named value1 and value2. Sum should print "The sum of __*value1*__ and __*value2*__ is __*sum*__ ", and Difference should print "The difference of __*value1*__ and __*value2*__ is __*difference*__ .
|
||||
|
||||
Call the methods from the main method multiple times with different values.
|
||||
|
||||
# Return Values
|
||||
|
||||
The return value of a method is __returned __ with the return keyword:
|
||||
|
||||
double radius = 2;
|
||||
|
||||
Console.WriteLine(CircleArea(radius)); // Outputs 12,56637...
|
||||
|
||||
double CircleArea(double radius)
|
||||
|
||||
{
|
||||
|
||||
double area = Math.PI * radius * radius;
|
||||
|
||||
return area;
|
||||
|
||||
}
|
||||
|
||||
In the first examples, the method declarations started with the void expression, which means that the method doesn't return anything
|
||||
|
||||
Here the method was declared as type double, which means that the method has to return a variable of type double
|
||||
|
||||
# Multiple Return Values
|
||||
|
||||
It is possible to return multiple values in the form of a __tuple__ :
|
||||
|
||||
(string, string) FirstAndLastName(string fullname)
|
||||
|
||||
{
|
||||
|
||||
string[] names = fullname.Split(' ');
|
||||
|
||||
return (names[0], names[names.Length - 1]);
|
||||
|
||||
}
|
||||
|
||||
(string first, string last) = FirstAndLastName("Hans \\"Dolph\\" Lundgren");
|
||||
|
||||
Console.WriteLine($"First name: {first}, last name: {last}");
|
||||
|
||||
// Outputs "First name: Hans, last name: Lundgren"
|
||||
|
||||
If only one value from a tuple is needed, you can use a __discard __ (\_)
|
||||
|
||||
// Only last name is allocated to a variable
|
||||
|
||||
(\_, string lastName) = FirstAndLastName("Arnold Alois Schwarzenegger");
|
||||
|
||||
# Scope of Methods
|
||||
|
||||
The variables declared inside of a method are __local variables__ and only accessible inside of that method
|
||||
|
||||
void ScopeExample()
|
||||
|
||||
{
|
||||
|
||||
// Variable 'a' is only accessible inside of this method
|
||||
|
||||
int a = 10;
|
||||
|
||||
a = 20; // This works
|
||||
|
||||
}
|
||||
|
||||
a = 30; // This throws an error
|
||||
|
||||
# Exercise 2
|
||||
|
||||
Write a method that takes a string as a parameter and prints the total number of spaces in that string.
|
||||
|
||||
Without using the string.Trim() method, modify the method so that it removes the spaces in a string and returns the new string.
|
||||
|
||||
(Hint: You can iterate a string just like arrays and lists.)
|
||||
|
||||

|
||||
|
||||
# Exercise 3
|
||||
|
||||
Write a method that takes a string as a parameter and prints each unique letter in that string.
|
||||
|
||||
Write a new method that takes a string as a parameter and prints the number of each unique letter in the string.
|
||||
|
||||

|
||||
|
||||
# Exercise 4
|
||||
|
||||
Write a method that takes a 10 digit number as a parameter and divides individual digits into even and odd arrays. Method should return both arrays.
|
||||
|
||||
Remember to check that method accepts only numbers.
|
||||
|
||||
# Assignments
|
||||
|
||||
[Assignments about this topic can be found here](https://gitea.buutti.com/education/academy-assignments/src/branch/master/C%23%20Basics/6.%20Methods)
|
||||
|
@ -0,0 +1,558 @@
|
||||
# Classes and Objects
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Namespaces
|
||||
|
||||
Classes
|
||||
|
||||
Object Variables
|
||||
|
||||
Access Specifiers
|
||||
|
||||
Class Members
|
||||
|
||||
Constructors
|
||||
|
||||
Value & Reference Types
|
||||
|
||||
Enum
|
||||
|
||||
# Namespaces
|
||||
|
||||
Using namespaces helps you to organize the scope of globally available data in your project, meaning that related objects in a well-named namespace makes it easy to import what you need
|
||||
|
||||
By default, when creating a new project and adding classes in Visual Studio, everything is contained within a namespace named after your project name
|
||||
|
||||
If you need a class or an enum to be accessible from anywhere, create it within that namespace and set it as public
|
||||
|
||||
// Note that namespaces can have
|
||||
|
||||
// subdirectories, as Models here
|
||||
|
||||
namespace MyAwesomeApp.Models
|
||||
|
||||
{
|
||||
|
||||
public class Student
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This is how you would use the
|
||||
|
||||
// Student class in other code
|
||||
|
||||
using MyAwesomeApp.Models
|
||||
|
||||
# Classes
|
||||
|
||||
__Classes __ in C# are blueprints for a __type __ of __object__
|
||||
|
||||
We have already used a class named _Program _ when creating a new console application:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
You could even create new instances of Program
|
||||
|
||||
* If we wanted, we could just write the entire program with thousands of lines of code on one page inside the main function
|
||||
* This kind of program would of course be impossible to work with within a group or organization:
|
||||
* No one should have to scroll through tens of thousands of lines of code to find that one line that causes problems…
|
||||
* At least make them browse through hundreds of classes instead :)
|
||||
* Classes provide __encapsulation __ which result in __shorter files__ , __less repetitive code__ and overall __better readability__
|
||||
|
||||
# Creating a Class
|
||||
|
||||
You can create a new class by writing
|
||||
|
||||
class Student
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Create variables inside a class just like before inside the Program class:
|
||||
|
||||
class Student
|
||||
|
||||
{
|
||||
|
||||
int id;
|
||||
|
||||
string name;
|
||||
|
||||
}
|
||||
|
||||
❕
|
||||
|
||||
Variables declared directly inside classes are called __fields.__
|
||||
|
||||
# Referencing a Class
|
||||
|
||||
New instances of a class can be created with the __new __ keyword
|
||||
|
||||
All instances of a class have exactly the same fields and methods, but they can have different values
|
||||
|
||||
In the following example a few __objects __ of __type __ Student are created
|
||||
|
||||
However, these objects have no functionality yet
|
||||
|
||||
namespace MyAwesomeApp
|
||||
|
||||
{
|
||||
|
||||
class Student
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Student student1 = new Student();
|
||||
|
||||
Student student2 = new Student();
|
||||
|
||||
Student student3 = new Student();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Object Variables
|
||||
|
||||
The variables inside of an object can be accessed with the '.' operator
|
||||
|
||||
However, the following syntax results in an error:
|
||||
|
||||
| Student class: | Program class: |
|
||||
| :-: | :-: |
|
||||
| class Student{ int id;} | class Program{static void Main(string[] args){ Student student = new Student(); student.id = 12345678;}} |
|
||||
|
||||

|
||||
|
||||
Let's fix the previous example by changing the _ _ __access specifier__ of the 'id' variable to __public__ :
|
||||
|
||||
| Student class: | Program class: |
|
||||
|:---------------------------------:|:--------------------------------------------------------------------------------------------------------------:|
|
||||
| class Student{ public int id;} | class Program{static void Main(string[] args){ Student student = new Student(); student.id = 12345678;}} |
|
||||
| | |
|
||||
|
||||
The value of the variable 'id' of the object 'student' is now 12345678
|
||||
|
||||
# Access Specifiers
|
||||
|
||||
__Access specifiers__ can be used to get additional level of protection inside classes
|
||||
|
||||
Variables specified with __private __ are accessible only inside the containing class
|
||||
|
||||
Variables specified with __public __ are accessible outside of the class
|
||||
|
||||
The __default __ access for any variable is private!
|
||||
|
||||
class Student
|
||||
|
||||
{
|
||||
|
||||
int id; // Accessible only inside the class
|
||||
|
||||
private string name; // Accessible only inside the class
|
||||
|
||||
public string address; // Accessible everywhere within the namespace
|
||||
|
||||
}
|
||||
|
||||
Continuing on the class in the previous slide, if we follow the 'student' variable with a dot, VS intellisense will only suggest the 'address' variable, because it was the only public variable of the 'Student' class
|
||||
|
||||

|
||||
|
||||
# Classes - Example
|
||||
|
||||
class Student
|
||||
|
||||
{
|
||||
|
||||
public int id;
|
||||
|
||||
public string name;
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Student student1 = new Student();
|
||||
|
||||
Student student2 = new Student();
|
||||
|
||||
student1.id = 22225555;
|
||||
|
||||
student1.name = "Johannes Kantola";
|
||||
|
||||
student2.id = 44441111;
|
||||
|
||||
student2.name = "Rene Orosz";
|
||||
|
||||
Console.WriteLine(student1.name); // Outputs Johannes Kantola
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Exercise 1
|
||||
|
||||
Create a console application that has a class User which contains two fields: string userName and string password
|
||||
|
||||
Create a main loop where the user is asked repeatedly for a new username and password. The values are stored to a new User instance and that instance is saved to a list of users
|
||||
|
||||
Print all stored usernames every time a new user is created
|
||||
|
||||
# Class Methods
|
||||
|
||||
As mentioned in the previous lecture, we can create methods inside of our classes:
|
||||
|
||||
class Person
|
||||
|
||||
{
|
||||
|
||||
public string firstName;
|
||||
|
||||
public string lastName;
|
||||
|
||||
public string FullName()
|
||||
|
||||
{
|
||||
|
||||
return firstName + " " + lastName;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Person someDude = new Person();
|
||||
|
||||
someDude.firstName = "Johannes";
|
||||
|
||||
someDude.lastName = "Kantola";
|
||||
|
||||
Console.WriteLine(someDude.FullName());
|
||||
|
||||
// Outputs Johannes Kantola
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
If the method is public, it can be called from outside of the class
|
||||
|
||||
# Constructors
|
||||
|
||||
Constructors are class methods which are called once the object is initialized
|
||||
|
||||
Constructors are created by creating a class method with the same name as the class:
|
||||
|
||||
class User
|
||||
|
||||
{
|
||||
|
||||
public User()
|
||||
|
||||
{
|
||||
|
||||
Console.WriteLine
|
||||
|
||||
("New user created!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
| | |
|
||||
| :-: | :-: |
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
User user = new User();
|
||||
|
||||
// Outputs "New user created!"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
❕
|
||||
|
||||
In Visual Studio, just write "ctor" and press tab twice to quickly create a constructor
|
||||
|
||||
# Constructors with Parameters
|
||||
|
||||
You can pass in parameters to the constructor at initialization:
|
||||
|
||||
class Car
|
||||
|
||||
{
|
||||
|
||||
private string model;
|
||||
|
||||
private int year;
|
||||
|
||||
private int doors;
|
||||
|
||||
public Car(string modelName, int modelYear)
|
||||
|
||||
{
|
||||
|
||||
model = modelName;
|
||||
|
||||
year = modelYear;
|
||||
|
||||
doors = 5;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
Car lada = new Car("Niva", 1984);
|
||||
|
||||
Car ford = new Car("Focus", 2010);
|
||||
|
||||
// Both cars have 5 doors by default
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Exercise 2
|
||||
|
||||
Create a console application with a class Animal, that contains
|
||||
|
||||
two strings: name and sound, and
|
||||
|
||||
a method Greet() that prints " __*name*__ says __*sound*__ !"
|
||||
|
||||
Create a few animals with different names and sounds
|
||||
|
||||
Call their greet methods from the main program
|
||||
|
||||
# Properties
|
||||
|
||||
When working with C#, you will eventually see __properties __ being used at some point
|
||||
|
||||
Properties allow the manipulation of private fields from outside of the class:
|
||||
|
||||
class User
|
||||
|
||||
{
|
||||
|
||||
private int id;
|
||||
|
||||
public int Id
|
||||
|
||||
{
|
||||
|
||||
get { return id; }
|
||||
|
||||
set { id = value; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
User admin = new User();
|
||||
|
||||
admin.Id = 12345678;
|
||||
|
||||
Console.WriteLine(admin.Id);
|
||||
|
||||
// This outputs 12345678
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Auto Properties
|
||||
|
||||
Auto properties are a shorthand version of the same:
|
||||
|
||||
class User
|
||||
|
||||
{
|
||||
|
||||
public int Id { get; set; }
|
||||
|
||||
}
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
User admin = new User();
|
||||
|
||||
admin.Id = 12345678;
|
||||
|
||||
Console.WriteLine(admin.Id);
|
||||
|
||||
// This outputs 12345678
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
❕
|
||||
|
||||
In Visual Studio, just write "prop" and press tab twice to quickly create an auto property
|
||||
|
||||
# Properties
|
||||
|
||||
* __Why use properties if we could just use public fields?__
|
||||
* We don't always want to expose the exact same variable that is used inside of the class, to outside
|
||||
* Properties allow class fields to be __read-only__ or __write-only__ :
|
||||
* public int Id { get; } // This field is read-onlypublic int Password { private get; set; } // This field is write-only
|
||||
* Properties are supported in __interfaces__ , while fields are not
|
||||
* We will cover interfaces later
|
||||
* There are a bunch of other reasons, but they are outside the scope of this course
|
||||
---
|
||||
|
||||
Lets say we have a variable in our class which is queued for calculating something important
|
||||
Now some other class changes that variable -> Problem
|
||||
|
||||
# Value and Reference Types
|
||||
|
||||
* If this program is executed, what will be the output?
|
||||
* int, double, char and other primitive data types are of __value type__
|
||||
* The variable stores the __actual data__ in memory
|
||||
* Every copy of the variable will be stored in a separate location
|
||||
* class Program{ static void Main(string[] args) { int user = 1; int otherUser = user; user = 2; Console.WriteLine( "Value of otherUser: " + otherUser); }}
|
||||
|
||||

|
||||
|
||||
* When this program is executed, what will be the output?
|
||||
* Strings, Arrays and Classes are of __reference type __
|
||||
* The variable stores the address of the data being stored in memory
|
||||
* Every copy of the variable points to the same address
|
||||
|
||||
class Program
|
||||
|
||||
{
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
User user = new User();
|
||||
|
||||
user.id = 1;
|
||||
|
||||
User otherUser = user;
|
||||
|
||||
user.id = 2;
|
||||
|
||||
Console.WriteLine(
|
||||
|
||||
"Value of otherUser.id: " + otherUser.id);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class User
|
||||
|
||||
{
|
||||
|
||||
public int id;
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
Location in memory
|
||||
|
||||
int userId = 42;
|
||||
|
||||
int otherUserId = userId;
|
||||
|
||||
| 42 |
|
||||
| :-: |
|
||||
| 42 |
|
||||
|
||||
Changing the value in the object will affect all variables referencing it!
|
||||
|
||||
| points to |
|
||||
| :-: |
|
||||
| points to |
|
||||
|
||||
User user = new User(42);
|
||||
|
||||
User otherUser = user;
|
||||
|
||||
| user { id = 42 } |
|
||||
| :-: |
|
||||
|
||||
# Exercise 3
|
||||
|
||||
Classes and enums can be created to a separate file. To quickly create a new class, right mouse click you project (not solution) name in solution explorer -> Add -> Class… Name your class "TestClass"
|
||||
|
||||
Back in your main method, create an instance of TestClass. The compiler should not give any errors.
|
||||
|
||||
Now rename the namespace that your TestClass is in, to "TestNamespace". Go back to your main method. The TestClass instantiation should not work anymore. Try to fix that without going back to TestClass.
|
||||
|
||||
# Exercise 4
|
||||
|
||||
Modify previous solution by using methods and class.
|
||||
|
@ -0,0 +1,302 @@
|
||||
# Inheritance & Abstract Classes
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
Inheritance
|
||||
|
||||
Abstract Classes
|
||||
|
||||
Enums
|
||||
|
||||
# Inheritance
|
||||
|
||||
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
|
||||
|
||||
Try to understand the following example:
|
||||
|
||||
| class Animal |
|
||||
| :-: |
|
||||
| -Eat()-Sleep() |
|
||||
|
||||
| class Dog : Animal |
|
||||
| :-: |
|
||||
| -Bark() |
|
||||
|
||||
| class Cat : Animal |
|
||||
| :-: |
|
||||
| -Meow() |
|
||||
|
||||
Animal can eat
|
||||
|
||||
Animal can sleep
|
||||
|
||||
Dog can eat
|
||||
|
||||
Dog can sleep
|
||||
|
||||
Dog can bark
|
||||
|
||||
Cat can eat
|
||||
|
||||
Cat can sleep
|
||||
|
||||
Cat can meow
|
||||
|
||||
# Inheritance - Example
|
||||
|
||||
class Animal
|
||||
|
||||
{
|
||||
|
||||
public void Eat() { Console.WriteLine("Eating..."); }
|
||||
|
||||
public void Sleep() { Console.WriteLine("Sleeping..."); }
|
||||
|
||||
}
|
||||
|
||||
class Cat : Animal
|
||||
|
||||
{
|
||||
|
||||
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..."); }
|
||||
|
||||
}
|
||||
|
||||
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!"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 }}
|
||||
|
||||
---
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
enum ProgramState
|
||||
|
||||
{
|
||||
|
||||
Login,
|
||||
|
||||
Menu,
|
||||
|
||||
Exit
|
||||
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
|
||||
{
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# Going Further: Object Oriented Programming
|
||||
|
||||
* 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
|
||||
* 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
|
||||
|
||||
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
|
||||
|
||||
Parable: The user of a microwave doesn't have to know about the complicated circuitry inside of the microwave. Only the buttons are revealed
|
||||
|
||||
# OOP: Polymorphism
|
||||
|
||||
This concept becomes more clear after we have covered interfaces
|
||||
|
||||
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
|
||||
|
@ -0,0 +1,270 @@
|
||||
# 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 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
|
||||
* Interfaces are way more commonly used than abstract classes
|
||||
|
||||
# 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
|
||||
* 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
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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 now
|
||||
|
||||
# Implementing an Interface (continued)
|
||||
|
||||
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 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
|
||||
|
||||
* 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
|
||||
|
||||
In those cases, inheriting from some shared base class wouldn't make sense
|
||||
|
||||
Consider the following example:
|
||||
|
||||
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?
|
||||
|
||||
| class Dog |
|
||||
| :-: |
|
||||
| -Eat()-Sleep()-WagTail() |
|
||||
|
||||
| class Human |
|
||||
| :-: |
|
||||
| -Eat()-Sleep()-Contemplate() |
|
||||
|
||||
| class Bear |
|
||||
| :-: |
|
||||
| -Eat()-Sleep()-Hibernate() |
|
||||
|
||||
All classes could inherit from a base class Animal, which has methods 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()?
|
||||
|
||||
| class Tree |
|
||||
| :-: |
|
||||
| -Grow()-Photosynthesize() |
|
||||
|
||||
| class Human |
|
||||
| :-: |
|
||||
| -Grow()-Move() |
|
||||
|
||||
| class Car |
|
||||
| :-: |
|
||||
| -Move()-Explode() |
|
||||
|
||||
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 - 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
|
||||
|
||||
interface IMovable
|
||||
|
||||
{
|
||||
|
||||
void Move();
|
||||
|
||||
}
|
||||
|
||||
interface ICarryable
|
||||
|
||||
{
|
||||
|
||||
void Carry();
|
||||
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||

|
||||
|
||||
# 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.
|
||||
|
||||
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
|
||||
|
||||
Create a list of type IInfo, and add the products and categories to it
|
||||
|
||||
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.
|
||||
|
||||
Start by creating 4 classes: __Shape__ , __Square__ , __Triangle__ and __Circle__ . Square, Triangle and Circle inherit from Shape. Shape implements the __IComparable\<Shape>__ __ __ interface.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
@ -1,64 +0,0 @@
|
||||
---
|
||||
marp: true
|
||||
paginate: true
|
||||
math: mathjax
|
||||
theme: buutti
|
||||
title: N. Example Lecture
|
||||
---
|
||||
|
||||
# Example Lecture
|
||||
|
||||
<!-- headingDivider: 5 -->
|
||||
<!-- class: invert -->
|
||||
|
||||
## Section
|
||||
|
||||
- This line appears instantly
|
||||
* This line appears by pressing spacebar
|
||||
* This line has an inline code `variable`
|
||||
```js
|
||||
console.log("Here's a coloured JavaScript code block");
|
||||
console.log("Remember indentation so it's revealed after the bullet point.");
|
||||
```
|
||||
* This line has an inline LaTeX maths equation $c = \frac{a^2}{\sqrt{b}}$
|
||||
* Here's a maths block:
|
||||
|
||||
$$
|
||||
F(x) = \int_a^b f(x) dx
|
||||
$$
|
||||
|
||||
<!-- _footer: Footers are exclusive to presentation; they are not shown in the webpage markdown document -->
|
||||
|
||||
## Columns
|
||||
|
||||
<div class='columns' markdown='1'>
|
||||
<div markdown='1'>
|
||||
|
||||

|
||||
|
||||
* Basic image example
|
||||
|
||||
</div>
|
||||
<div markdown='1'>
|
||||
|
||||

|
||||
* Wider image
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
* This line is outside the columns and goes from left all the way to the right
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
* In VS Code, install the extensions
|
||||
* [Marp for VS code](https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode)
|
||||
* So you can see the slideshow preview when editing.
|
||||
* [Markdown all in one](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one)
|
||||
* [Markdown table formatter](https://marketplace.visualstudio.com/items?itemName=fcrespo82.markdown-table-formatter)
|
||||
* *Right click > Format document* makes tables pretty
|
||||
* [Save and run](https://marketplace.visualstudio.com/items?itemName=wk-j.save-and-run)
|
||||
* An HTML version of the lecture is created on save
|
||||
* See [settings.json](./.vscode/settings.json)
|
||||
* Add filenames to `notMatch` if a HTML on save is not needed
|
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 9.7 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 5.5 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 6.7 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 1.5 KiB |