Zugriffs-Ebenen von Membern
Mit Zugriffsebenen gibt man an von welchen Stellen im Code auf Elemente zugegriffen werden kann.
Dient für die überschaubarkeit und sicherheit des Codes.
Public -> Keine Zugriffseinschränkung
Private -> Zugriff auf die Klasse beschränkt
Protected -> Zugriff auf Klasse und davon abgeleitete Typen beschränkt
Internal -> Zugriff auf die Assembly beschränkt
Protected Internal -> Zugriff auf die aktuelle Assembly oder die von der enthaltenen Klasse abgeleiteten Typen beschränkt
____________________________________________________________________________
Werte- und Referenztypen
Wertetypen sind die primitiven Datentypen (integer, float), Structs und Enums
Referenztypen sind Strings, Arrays und Klassen
Unterschied liegt in der Art und Weise wie die Werte gespeichert werden
Wertetypen -> Werden auf dem Stack gespeichert
Referenztypen -> Werden auf dem Heap gespeichert
Stack ist ein bereich des RAMs. Speicher ist sehr effizient und schnell
Heap ist ein nicht gut strukturierter bereich des RAMs, weniger effizient. Speicher muss wieder freigegeben werden wenn nicht mehr benötigt. Referenztypen belasten den Speicher mehr, muss vom Garbage-Collector freigegeben werden.
Wertetypen enthalten immer den tatsächlichen Wert
Referenztypen enthalten nur einen Zeiger/Pointer mit der Referenz auf das Objekt
____________________________________________________________________________
Konstruktor
Der Konstruktor ist die erste Methode die in einem Objekt aufgerufen wird
Er wird dazu verwendet Eigenschaften und Variablen zu initialisieren
Wird mit dem "new" Schlüsselwort eingeleitet, wird bei der erstellung eines Objekts aufgerufen
Person person1 = new Person();
Ihm können auch Parameter übergeben werden
Person person1 = new Person("Sabine", "Müller", 34);
Konstruktor Definition
Muss nicht selbst definiert werden, wenn man diesen nicht braucht. Es wird ein leerer Konstruktor automatisch erzeugt, sollte kein eigener definiert werden
class Person {
//Eigenschaften
public string Vorname {get; set;}
public string Nachname {get; set;}
public int Alter {get; set;}
//Konstruktor
public Person(string vorname, string nachname, int alter){
Vorname = vorname;
Nachname = nachname;
Alter = alter;
}
}
Schnittstellen/Interfaces
Eine Klasse, die eine Schnittstelle implementiert, muss alle vorgegebenen Member enthalten.
Eine Schnittstelle kann folgende Dinge vorschreiben:
-Methoden
-Eigenschaften
-Ereignisse (Events)
-Indexer
Die eigentliche Funktionalität der Member wird in der Klasse selbst codiert.
interface ITier
{
//Eigenschaften
int Alter {get; set;}
string Geschlecht {get; set;}
//Methoden
void Essen();
void Trinken();
}
class Löwe : ITier
{
//Eigenschaften
//Methoden
}
Abstrakte Klassen und Methoden
Eine abstrakte Klasse ist eine nicht instanziierbare Klasse, die nur dazu dient geerbt zu werden.
Schnittstellen können Member nur vorgeben, während abstrakte Klasse auch tatsächlichen Code enthalten können.
Man verwendet abstrakte Klassen auch als Basisklassen.
Polymorphismus
Statische Polymorphie
Mit der statischen Polymorphie ist das Überladen von Methoden gemeint.
Dynamische Polymorphie
Mit der dynamischen Polymorphie ist das Überschreiben von abstrakten Methoden gemeint. Hierfür kann auch das Schlüsselwort "virtual" verwendet werden.
Structs
Ein Struct-Typ ist ein Typ in C#, welcher dazu verwendet wird, zusammengehörende Variablen zu gruppieren, um somit kompakte Objekte darzustellen.
struct Point
{
public int x;
public int y;
}
Strukturen sind eine speicherfreundliche "Alternative" zu Klassen, da sie Wertetypen sind und somit auf dem Stack-Speicher verwaltet werden.
Ein Struct ist nicht dasselbe wie eine Klasse! (Klasse = Referenztyp, Struct = Wertetyp).
Enums
Mit Enums können wir unsere eigenen Datentypen definieren. Sie sind eine Sammlung aus Konstanten auf die man einzenln über den Enum zugreifen kann.
enum Himmelsrichtung
{
Nord = 0,
Ost = 1,
Süd = 2,
West = 3
}
static void Main(string[] args)
{
Himmelsrichtung richtung = Himmelsrichtung.Nord;
Console.WriteLine(richtung); //OUTPUT: "Nord"
Console.WriteLine((int)richtung); //OUTPUT: "0"
}
Collections
Collections sind Klassen, welche das Speichern von mehreren Werten on einem Objekt ermöglichen (ähnlich wie bei Arrays).
Sie sind flexibler als Arrays, da ihre Größe nicht beim Kompilieren feststehen muss.
-Arrays haben eine statische Grüße
-Collections haben eine dynamische Größe
-Lists - ähnelt dem Array am meisten, gleiche Funktion nur flexibler und dynamisch, Größe passt sich an.
//Erstelle die Liste
List<string> namensListe = new List<string>();
//Alternativ
List<string> namensListe = new List<string>()
{
"Alina",
"Hendrik",
"Janek",
"Isabell"
};
//Füge Werte zur Liste hinzu
namensListe.Add("Janek");
namensListe.Add("Hendrik");
namensListe.Add("Sandra");
//Werte löschen
namensListe.Remove("Janek");
//Index löschen
namensListe.RemoveAt(2);
//Lese die Werte per Schleife aus
foreach(string name in namensListe)
{
Console.WriteLine(name);
}
Stacks
Stacks sind eine sehr einfache Art von Collection, welche man mit einem Stapel Karten vergleichen kann.
Stacks haben dabei 3 wichtige Methoden:
-Push() -> Zum Hinzufügen von Werten auf den Stapel.
-Pop() -> Gibt den obersten Wert auf dem Stapel zurück und löscht ihn dann.
-Peek() -> Gibt den obersten Wert auf dem Stapel zurück ohne ihn zu löschen.
Der oberste Wert auf dem Stapel ist immer der Wert, der zuletzt hinzugefügt wurde.
//Stack erstellen
Stack<int> numberStack = new Stack<int>();
//Werte hinzufügen
numberStack.Push(1);
numberStack.Push(2);
numberStack.Push(3);
//Obersten Wert lesen und löschen
Console.WriteLine(numberStack.Pop()); //OUTPUT: 3
Console.WriteLine(numberStack.Pop()); //OUTPUT: 2
//Obersten Wert lesen
Console.WriteLine(numberStack.Peek()); //OUTPUT: 1
Console.WriteLine(numberStack.Peek()); //OUTPUT: 1
Queues
Queues sind eine Datenstruktur welche man mit einer Warteschlange vergleichen kann.
Die Queue-Klasse enthält 3 wichtige Methoden:
-Enqueue() -> Zum hinzufügen eines Wertes zur Warteschlange.
-Dequeue() -> Zum lesen und löschen des ersten Wertes in der Warteschlange.
-Peek() -> Zum lesen des ersten Wertes ohne ihn zu löschen.
Beim lesen wird immer der Wert zurückgegeben der am frühesten hinzugefügt wurde!
//Queue erstellen
Queue<string> personQueue new Queue<string>();
//Werte hinzufügen
personQueue.Enqueue("Peter");
personQueue.Enqueue("Alina");
personQueue.Enqueue("Sandra");
personQueue.Enqueue("Sabrina");
//Ersten Wert lesen und löschen
Console.WriteLine(personQueue.Dequeue()); //OUTPUT: Peter
Console.WriteLine(personQueue.Dequeue()); //OUTPUT: Alina
Console.WriteLine(personQueue.Dequeue());//OUTPUT: Sandra
//Ersten Wert lesen
Console.WriteLine(personQueue.Peek()); //OUTPUT: Sabrina
Console.WriteLine(personQueue.Peek()); //OUTPUT: Sabrina
Dictionaries
Dictionaries sind eine Datenstruktur, welche man mit einem Wörterbuch vergleichen kann.
Die Werte die darin gespeichert werden, bestehen aus einem Key und einem Value.
Key und Value können dabei von einem beliebigen Datentyp sein.
//Dictionary erstellen
Dictionary<int, string> kunden = new Dictionary<int, string>();
//Werte hinzufügen
kunden.Add(1000, "Sebastian");
kunden.Add(1001, "Alina");
kunden.Add(1002, "Franz");
kunden.Add(1003, "Peter");
//Werte löschen
kunden.Remove(1003);
//Zusätzliche Logik um Fehler bei Abfrage von Fehlerhaften Key Werten zu verhindern
if (kunden.ContainsKey(1003)){
Console.WriteLine(kunden[1003]);
}
//Werte Lesen
Console.WriteLine(kunden[1002]); //OUTPUT: "Franz"
//Dictionaries mit Schleifen durchlaufen
foreach(KeyValuePair<int, string> kundenInfo in kunden)
{
Console.WriteLine(kundenInfo.Key, kundenInfo.Value);
}
Generics
In der Programmierung dienen Variablen als Platzhalter für Werte.
Generics dienen als Platzhalter für Datentypen.
Generics kommen dann zum Einsatz, wenn man zum Zeitpunkt der Entwicklung noch nicht ganz genau weiß, welchen Datentypen man für eine Varible im Code verwenden möchte.
//Generic T erstellen
class Wertebehälter<T>
{
//Eigenschaft vom typ "T" erstellen, dient als Platzhalter
//Der Datentyp wird erst bei der Erstellung eines Objektes festgelegt
public T MeinWert { get; set; }
public Wertebehälter(T wert)
{
MeinWert = wert;
}
public void WertAusgabe()
{
Console.WriteLine(MeinWert.ToString());
}
}
static void Main(string[] args)
{
//Der Datentyp "string" wird dem Platzhalter zugewiesen
Wertebehälter<string> behälter = new Wertebehälter<string>("Janek");
behälter.WertAusgabe(); //OUTPUT: "Janek"
}
Delegaten
Ein Delegat ist ein Funktionszeiger.
Er stellt einen Typen dar, in welchen man die Referenz zu einer Methode speichern kann.
Delegaten ermöglichen also das Speichern von Methodenreferenzen in einer Variable, über welche man die Methode dann jederzeit aufrufen kann!
Bei der Definition des Delegaten wird der Rückgabetyp und die Methoden-Signatur angegeben.
Um eine Methode in einen Delegaten zu schreiben muss diese mit der Delegaten-Definition kompatibel sein.
//Delegaten definieren
public delegate double Rechenoperation(double x, double y);
//Methode erstellen die kompatibel ist mit der Definition der Delegate
static double Addition(double x, double y)
{
return x+y;
}
//Methodenreferenz wird in die Delegate gespeichert
static void Main(string[] args)
{
Rechenoperation rechenoperation = new Rechenoperation(Addition);
double ergebnis = rechenoperation(10,5);
}
Man verwendet Delegaten dann, wenn zur Laufzeit entschieden werden muss, welche Methode ausgeführt werden soll.
Mit Delegaten kann man Methoden definieren, die andere Methoden als Parameter annehmen und intern aufrufen können.
Try-Catch
Mit Try-Catch kann man Fehler zur Laufzeit abfangen und auf diese reagieren.
Das Konstrukt besteht aus mindestens zwei zusammenhängenden Code-Blöcken.
In den Try Block kommt der Code hinein, der einen Fehler auslösen könnte.
In den Catch Block kommt der Code hinein, mit welchen auf einen eventuellen Fehler reagiert wird.
Im Catch Block können wichtige Daten noch abgespeichert werden oder es kann zurück zum Anfang der Fehlerauslösenden Operation gesprungen werden.
try
{
int[] zahlen = new int[3];
zahlen[0] = 0;
zahlen[1] = 1;
zahlen[2] = 2;
zahlen[3] = 3;
}
catch (InddexOutOfRangeException ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Du hast zu viele Indizes befüllt!");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Die Exceptions müssen der Reihe nach immer allgemeiner werden.
Der Finally-Block kann als letzter Block an ein Try-Catch-Konstrukt angehängt werden.
Der sich im Finally-Block befindliche Code wird IMMER ausgelöst werden, egal ob eine Exception ausgelöst wurde oder nicht.
Er wird normalerweise dazu verwendet, um fremde Ressourcen vor dem Verlassen einer Methode wieder freizugeben(Datenbankverbindungen, Datenströme, usw...)
static void WriteToFile()
{
try
{
//Schreibe auf eine Datei
}
catch (Exception ex)
{
//Speichere bereits gesammelte Daten
//Informiere den User über den Fehler
return; //Beende die Methode
}
finally
{
//Trenne die Verbingung zur Datei
//Gebe die fremden Ressource wieder frei
}
//Schreibe Daten in eine Log-Datei
}
Math-Klasse
Häufig verwendete Methoden:
-Abs(x) -> "Absolute" Gibt einen Wert in positiv zurück.
-Sign(x) -> "Signum" Gibt entweder -1, 0 oder 1 zurück.
-Pow(x) -> "Power of X" Potenziert einen Wert.
-Sqrt(x) -> "Square Root" Quadratwurzel
-Round(x,y) -> Runden auf Nachkommastellen
-Ceiling(x) -> Aufrunden auf Ganzzahl
-Floor(x) -> Abrunden auf Ganzzahl
Die Math-Klasse stellt neben den Methoden auch zwei Konstanten zur Verfügung:
PI -> Das Verhältnis eines Kreisumfangs zum Durchmesser
E -> Die Basis des natürlichen Logarithmus
Random-Klasse
Ist eine Klasse, die das erzeugen von zufälligen Zahlen ermöglicht.
//Erzeuge Random Objekt
Random rnd = new Random();
//Generiere zufällige Zahl zwischen 1 und 3
int zahl = rnd.Next(1, 4);
//Gebe die Zahl aus
Console.WriteLine(zahl);
Die Klasse stellt 3 relevante Methoden für die Erzeugung von zufälligen Werten bereit:
Random.Next() -> Gibt eine zufällige Ganzzahl zurück oder eine Zufallszahl in einem Bereich
Random.NextBytes() -> Befüllt ein ByteArray mit Zufallszahlen
Random.NextDouble() -> Gibt eine zufällige Zahl von 0.0 bis 1.0 zurück
System.IO
Ist ein Namespace welcher Klassen für die Arbeit mit Dateien, Verzeichnissen und Datenströmen bereitstellt.
Sein Aufgabenbereich kann man in 2 Kategorien aufteilen:
1. Arbeit mit Dateien und Verzeichnissen
2. Datentransport mithilfe von Streams
Wichtige Klassen:
-Directory und DirectoryInfo -> Zum Arbeiten mit Verzeichnissen
-File und FileInfo -> Zum Arbeiten mit Dateien
-Path -> Zum Arbeiten mit Pfaden
-Stream, FileStream, StreamReader, StreamWriter -> Klassen für Datenströme
Directory/DirectoryInfo
Sie ermöglichen das Erstellen/Verschieben und Auflisten von Verzeichnissen.
Die Klassen haben Eigenschaften welche Informationen über das Verzeichnis enthalten.
Eigenschaften
Name -> Name des Verzeichnisses
FullName -> Voller Name (kompletter Pfad) des Verzeichnisses
CreationTime -> Zeitpunkt der Erstellung des Verzeichnisses
Exists -> Gibt an ob das Verzeichnis existiert
Methoden
Create() -> Erstellt ein Verzeichnis
CreateSubdirectory() -> Erstellt ein Unterverzeichnis
Delete() -> Löscht das Verzeichnis und seine Dateien/Unterverzeichnisse
GetDirectories() -> Gibt die Unterverzeichnisse des aktuellen Verzeichnisses zurück
GetFiles() -> Gibt eine Dateiliste des aktuellen Verzeichnisses zurück
Refresh() -> Aktualisiert den Zustand des Objekts
File/FileInfo
Sie stellen Eigenschaften und Methoden zum Erstellen, Kopieren, Löschen, Verschieben und Öffnen von Dateien bereit.
Eigenschaften
Attributes -> Ruft die Attribute der Datei ab (z.B) ReadOnly
Name -> Gibt den Namen der Datei zurück
FullName -> Gibt den ganzen Pfad der Datei zurück
Extension ->Gibt die Dateienendung zurück (.txt, .png, .mp3, usw)
Length -> Ruft die Größe in Byte ab
Exists -> Gibt an ob die Datei existiert
Methoden
Create -> Erstellt eine Datei
Delete() -> Löscht eine Datei unwiederruflich
AppendText() -> Erstellt einen Streamwriter, der der Datei einen Text anfügt
MoveTo() -> Zum verschieben oder umbenennen von Dateien
Encrypt() -> Verschlüsselt eine Datei
Decrypt() -> Entschlüsselt eine Datei
Steams
Ein Stream ermöglicht den Transport von Daten zu und von einer Datenquelle.
Zu diesen Datenquellen gehören z.B.:
-Dateien (FileStream)
-Der Speicher (MemoryStream)
-Ein Netzwerk (NetworkStream)
All diese Stream-Klassen erben von der Abstrakten Basisklasse "Stream".
Helfer Klassen:
Für die Arbeit mit Streams gibt es Helfer-Klassen, welche das Umwandeln der Bytes in andere Formate automatisch erledigen.
-StreamReader -> Zum lesen von Streams
-StreamWriter -> Zum schreiben auf Streams
Diese Klassen erzeugen automatisch einen FileStream mit dem gearbeitet wird. Es ist also nur die Erzeugung eines Objektes der Helferklassen nötig.