Arbeiten mit Strings

  • Code
  • Erklärung
  • Ergebnis

using System;
using System.Text;

namespace test
{
    class Program
    {
        static void Main(string[] args)
        {
            string first = "Einfaches Beispiel ";
            string second = "von Strings.";
            string third = first + second;
            Console.WriteLine(third);

            //Contains 
            string s1 = "Das ist ein Beispiel Text.";
            string s2 = "ein";
            bool exists = s1.Contains(s2);
            Console.WriteLine("'{0}' beinhaltet '{1}': {2}", s1, s2, exists);
            //Substring
            Console.WriteLine("'{0}' ab der Position 5 lautet: {1}", s1, s1.Substring(5));

            //Equals
            Console.WriteLine("'{0}' und '{1}' ist gleich: {2}", s1, s2, s1.Equals(s2));

            //Remove
            string s = "abc----hij";
            Console.WriteLine("\nIndex: 0123456789");
            Console.WriteLine("1)     {0}", s);
            Console.WriteLine("2)     {0}", s.Remove(3));
            Console.WriteLine("3)     {0}", s.Remove(3, 4));

            //ToUpper und ToLower
            string text = "EiN kaPUTter TeXt";
            Console.WriteLine("Groß geschrieben: {0}", text.ToUpper());
            Console.WriteLine("Klein geschrieben: {0}", text.ToLower());

            //Trim
            string withSpaces = "     Leerzeichen Enthalten   ";
            Console.WriteLine("Getrimmt: {0}", withSpaces.Trim());

            // Caps Lock umdrehen mit dem StringBuilder.
            string wrong = "uNABSICHLICH cAPS lOCK eINGESCHALTET.";
            StringBuilder sb = new StringBuilder(wrong);

            for (int i = 0; i < sb.Length; i++)
            {
                if (Char.IsLower(sb[i]) == true)
                    sb[i] = Char.ToUpper(sb[i]);
                else if (Char.IsUpper(sb[i]) == true)
                    sb[i] = Char.ToLower(sb[i]);
            }
            string correct = sb.ToString();
            Console.WriteLine("\nOriginal: {0}", wrong);
            Console.WriteLine("Korrigiert: {0}", correct);
        }
    }
}


Die String-Klasse ist eine der am häufigsten genutzten Klassen mit vielen Methoden und Verwendungsmöglichkeiten, weshalb wir ihr an dieser Stelle einen eigenen Bereich widmen.
Wie bereits in den Grundlagen beschrieben werden Strings wie gewöhnliche Variablen initialisiert und deklariert. Auch das Zusammenführen von mehreren Strings ist denkbar einfach. Hierzu wird einfach der + Operator verwendet um zwei String aneinander zu hängen. Wir möchten an dieser Stelle nur die wichtigsten Eigenschaften der Stringklasse erwähnen die man als angehender Entwickler kennen sollte. Für eine komplette Dokumentation verwende bitte die MSDN

Zu den wichtigsten Methoden des Stringklasse zählen:
  • Length (liefert die Länge des Strings)
  • Contains (liefert true oder false ob ein substring in einem String enthalten ist)
  • Substring (liefert einen Teilstring ab einer definierten Position)
  • Equals (liefert true oder false ob sich zwei Strings gleichen. Groß- und Kleinschreibung wird berücksichtigt)
  • Remove (entfernt der Rest eines Strings ab einer bestimmten Position oder eine Anzahl an Zeichen ab einer bestimmten Position)
  • ToUpper / ToLower (Liefert den String groß oder klein geschrieben)
  • Trim (Entfernt Leerzeichen zu Beginn und Ende des gegebenen Strings)
Eine weitere Besonderheit bildet die StringBuilder Klasse die uns bei der Erstellung neuer Strings behilflich ist.
Da Die klassischen String-Methoden den ursprünglichen String unverändert lassen (Rückgabe dieser Methoden ist wieder ein neuer String) kann mit Hilfe des StringBuilders ein vorhandener String modifiziert werden.
In diesem Beispiel drehen wir die Groß-/Kleinschreibung einer Zeichenkette um. (using System.Text; benötigt)

Klassen

  • Code
  • Erklärung
  • Ergebnis

using System;

namespace SampleClass
{
    public class Car
    {
        //Klassenvariablen
        private string brand;
        private string model;
        private int power;
        private string color;

        //Konstruktor
        public Car(string s)
        {
            brand = s;
        }

        // Eigenschaften
        public int Power
        {
            get { return power; }
            set { power = value; }
        }

        public string Color
        {
            get { return color; }
            set { color = value; }
        }

        public string Brand
        {
            get { return brand; }
            set { brand = value; }
        }
        public string Modell
        {
            get { return model; }
            set { model = value; }
        }

        public void StartEngine()
        {
            Console.WriteLine("Der {0} faehrt los.", brand);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            //Erstellen eines neuen Car Objektes
            Car myCar = new Car("Mitsubishi");

            //Vergeben einer Farbe und Starten des Motors
            myCar.Color = "Schwarz";
            myCar.StartEngine();

            Console.WriteLine("Die Farbe des Autos ist {0}", myCar.Color);
        }
    }
}


Bevor du dich mit dem folgenden Beispiel widmest, mache dich bitte mit dem Konzept von objektorientierter Programmierung vertraut. Ein klares Verständnis von Klassen ist für einen Programmierer unabdinglich. Einen passende Quelle bietet unser Artikel zu Objektorientierung.

Zu Beginn widmen wir uns der Instanziierung einer Klasse und die Verwendung ihrer Methoden.
In diesem Beispiel haben wir eine Klasse "Car" erstellt. Zu Beginn definieren wir einige Variablen die wir als private deklarieren. Den Zugriff auf diese Variablen ermöglichen wir über sogenannte getter und setter. Es ist empfohlen Klassenvariablen stets über deren Eigenschaftsmethoden erreichbar zu machen. Der Vorteil daran ist unter anderem, dass sich so leicht Prüfungen auf die Korrektheit der Werte implementieren lässt und der Zugriff klar gesteuert werden kann.

Jede Klasse besitzt von Haus aus einen Standardkonstruktor der uns unser Objekt instanziiert. Der Konstruktor besitzt keinen typischen Rückgabewert und muss immer den Klassennamen als Bezeichnung haben. In diesem Besipiel haben wir den Standardkonstruktor mit einem String-Parameter erweitert um zu gewährleisten, dass jedes Auto-Objekt zumindest einen Markennamen beinhaltet. Der Standardkonstruktor wird somit ersetzt und existiert nicht mehr.

Solltest du weiterhin einen Parameterlosen Konstruktor benötigen, muss dieser als public Car() {} implementiert werden. Zuletzt besitzt diese Klasse noch eine einfache Methode StartEngine() die eine Ausgabe in der Console veranlasst.

In unserer Main-Methode können wir nun mit dem Schlüsselwort new ein neues Car-Objekt erschaffen. Wir nennen es "myCar" und übergeben dem Konstruktor „Mitsubishi“ als Markennamen. Mit einem einfachen Punk können wir nun auf die Eigenschaften und Methoden unseres Objektes zugreifen.

Vererbung

  • Code
  • Erklärung
  • Ergebnis

using System;

namespace SampleClass
{
    //Abstrakte Basisklasse
    public abstract class Kfz
    {
        //Klassenvariablen
        private string brand;
        private string model;
        private int power;
        private string color;

        // Eigenschaften
        public int Power
        {
            get { return power; }
            set { power = value; }
        }

        public string Color
        {
            get { return color; }
            set { color = value; }
        }

        public string Brand
        {
            get { return brand; }
            set { brand = value; }
        }
        public string Modell
        {
            get { return model; }
            set { model = value; }
        }

        public abstract void StartEngine();
    }

    //Erbende Klasse Pkw
    public class Pkw : Kfz
    {
        private int numDoors;

        public int NumDoors
        {
            get { return numDoors; }
            set { numDoors = value; }
        }

        public Pkw(string s)
        {
            Brand = s;
        }

        public override void StartEngine()
        {
            Console.WriteLine("Der PKW {0} startet.", Brand);
        }

    }

    //Erbende Klasse Lkw
    public class Lkw : Kfz
    {
        private int maxWeight;

        public int MaxWeight
        {
            get { return maxWeight; }
            set { maxWeight = value; }
        }

        public Lkw(string s)
        {
            Brand = s;
        }

        public override void StartEngine()
        {
            Console.WriteLine("Der LKW {0} startet.", Brand);
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            //Erstellen neuer Pkw und Lkw Objekte
            Pkw myPKW = new Pkw("Mitsubishi");
            Lkw myLKW = new Lkw("MAN");

            //Vergeben einer Farbe und Starten des Motors des Pkw
            myPKW.Color = "Schwarz";
            myPKW.StartEngine();

            //Vergeben einer Farbe und Starten des Motors des Lkw
            myLKW.MaxWeight = 50;
            myLKW.StartEngine();

            Console.WriteLine("Die Farbe des PKW ist {0}.", myPKW.Color);
            Console.WriteLine("Das maximale Gewicht des LKW ist {0} Tonnen.", myLKW.MaxWeight);
        }
    }
}

Bevor du dich mit dem folgenden Beispiel widmest, mache dich bitte mit dem Konzept von objektorientierter Programmierung vertraut. Ein klares Verständnis von Klassen ist für einen Programmierer unabdinglich. Einen passende Quelle bietet unser Artikel zu Objektorientierung.

Für dieses Beispiel verwenden wir die Klasse von vorhin benennen sie aber in "Kfz" um.
Wir erweitern sie um das Schlüsselwort abstract um zu verhinden, dass ein Objekt vom Typ Kfz instanziiert werden kann. Wird eine Klasse als abstract markiert kann die Klasse nur von durch die Vererbung an eine andere Klasse verwendet werden.
Weiters haben wir in der Basisklasse die Methode StartEngine() als abstract markiert um zu erzwingen, dass jede erbende Klasse eine eigene Implementierung dieser Methode beinhaltet. Beachte, dass eine abstrakte Methode selbst keinen Inhalt hat sondern nur den Methodennamen und etwaige Paramter vorgibt.

Wie du siehst enthält jede erbende Klasse die Eigenschaften ihrer Basisklasse. Somit können wir die Farbe eines PKW aufrufen obwohl die PKW-Klasse selbst keine Implementierung beinhaltet. Somit lassen sich leicht gemeinsame Eigenschaften vom LKW und PKW in einer gemeinsamen Basisklasse implementieren.

Polymorphie

  • Code
  • Erklärung
  • Ergebnis

using System;

namespace SampleClass
{
    //Abstrakte Basisklasse
    public abstract class Kfz
    {
        //Klassenvariablen
        private string marke;
        private string modell;
        private int leistung;
        private string farbe;

        // Eigenschaften
        public int Leistung
        {
            get { return leistung; }
            set { leistung = value; }
        }

        public string Farbe
        {
            get { return farbe; }
            set { farbe = value; }
        }

        public string Marke
        {
            get { return marke; }
            set { marke = value; }
        }
        public string Modell
        {
            get { return modell; }
            set { modell = value; }
        }

        public abstract void StartEngine();
    }

    //Erbende Klasse Pkw
    public class Pkw : Kfz
    {
        private int anzahlTueren;

        public int AnzahlTueren
        {
            get { return anzahlTueren; }
            set { anzahlTueren = value; }
        }

        public Pkw(string s)
        {
            Marke = s;
        }

        public override void StartEngine()
        {
            Console.WriteLine("Der PKW {0} startet.", Marke);
        }

    }

    //Erbende Klasse Lkw
    public class Lkw : Kfz
    {
        private int maxGewicht;

        public int MaxGewicht
        {
            get { return maxGewicht; }
            set { maxGewicht = value; }
        }

        public Lkw(string s)
        {
            Marke = s;
        }

        public override void StartEngine()
        {
            Console.WriteLine("Der LKW {0} startet.", Marke);
        }

    }
    
    //Erbende Klasse Motorrad
    public class Motorrad : Kfz
    {
        private int maxGeschwindigkeit;

        public int MaxGewicht
        {
            get { return maxGeschwindigkeit; }
            set { maxGeschwindigkeit = value; }
        }

        public Motorrad(string s)
        {
            Marke = s;
        }

        public override void StartEngine()
        {
            Console.WriteLine("Das Motorrad {0} startet.", Marke);
        }

    }


    class Program
    {
        static void Main(string[] args)
        {
            //Erstellen eines Kfz Arrays
            Kfz[] arr = new Kfz[3];
            
            //Befuellen des Arrays mit Objekten
            arr[0] = new Pkw("Mitsubishi");
            arr[1] = new Lkw("MAN");
            arr[2] = new Motorrad("Yamaha");

            //Starten des Motors abhaengig der jeweilige Implementierung
            foreach (Kfz temp in arr)
                temp.StartEngine();
        }
    }
}

Bevor du dich mit dem folgenden Beispiel widmest, mache dich bitte mit dem Konzept von objektorientierter Programmierung vertraut. Ein klares Verständnis von Klassen ist für einen Programmierer unabdinglich. Einen passende Quelle bietet unser Artikel zu Objektorientierung.

Die Polymorphie ermöglicht uns, dass Objekte gemeinsamer Basisklasse, je nach ihrer eigenen Implementierung, unterschiedlich auf den gleichen Befehl reagieren. Somit können wir, wie dem vorherigen Beispiel entsprechend, unterschiedliche Kfz-Objekte erstellen und gemeinsam ansprechen ohne dabei die konkrete Implementierung der einzelnen Klassen aufrufen zu müssen.

Wir verwenden wieder den Code des vorherigen Beispiels und erweitern ihn wie folgt:
Zuerst erstellen wir eine Motorradklasse die ebenfalls von Kfz erbt und eine eigene StartEngine() Implementierung hat.
Als nächstes befüllen wir ein kurzes Array mit Kfz-Objekten und lassen per foreach-Schleife die StartEngine() Methode aufrufen.
Wie du siehst wird die korrekte Implementierung der jeweiligen Klasse aufgerufen.

Interfaces

  • Code
  • Erklärung
  • Ergebnis

public interface IMoveable
{
    int Wheels { get; set; }

    void Move();
}

class Kfz : IMoveable
{
    private int wheels;
    public int Wheels
    {
        get { return wheels; }
        set { wheels = value; }
    }

    public void Move()
    {
        Console.WriteLine("Das Kfz bewegt sich");
    }
}

Das Konzept der Schnittstellen oder Interfaces ist dem der Klassen nicht ganz unähnlich.
Eine Klasse ist eine Art Schablone in der Methoden und Eigenschaften ihrer Instanzen definiert sind. Eine Schnittstelle hingegen ist als Vorgabe für Klassen zu sehen die eine Implementierende Klasse beinhalten muss.
Während C# keine Mehrfachvererbung unterstützt (also das Erben von mehreren Basisklassen) kann dies durch das implementieren mehrerer Schnittstellen realisiert werden.
Eine Schnittstelle enthält hierbei keine ausprogrammierten Methoden sondern nur abstrakte Definitionen und ggf. Eigenschaften die allerdings nur aus einem Getter und Setter bestehen dürfen.

Wie du im Codebeispiel erkennen kannst, haben wir unsere Schnittstelle IMoveable genannt. Konventionsgemäß beginnen Schnittstellen immer mit einem großen i. Die Schnittstelle enthält keine Methoden oder Felder sondern nur Eigenschaften und eine nicht ausprogrammierte Methode. Diese werden erst in der Implementierenden Klasse Kfz auscodiert.

Einfach erklärt haben Schnittstellen zwei Vorteile. Zum einen ermöglichen sie die fehlende Mehrfachvererbung in C# und zum anderen kann so einer Klasse ein Typ vorgegeben werden.
Einfaches Beispiel: Unsere Klasse Kfz von oben könnte unsere Schnittstelle implementieren. Ebenso könnte es eine Klasse Fahrrad und Skateboard geben die diese Schnittstelle ebenfalls implementieren. Auch wenn diese Klassen komplett unterschiedlich sind, haben sie nun gemeinsam, dass sie unter anderem per Move() bewegbar sind.
Wie bei der Polymorphie erklärt können nun alle Instanzen von Klassen die diese Schnittstelle implementieren per foreach(IMoveable tmp in arr) angesprochen werden.

Streams

  • Code
  • Erklärung
  • Ergebnis

using System;
using System.IO;
using System.Collections.Generic;

namespace StreamExample
{
    class Program
    {
        static void Main(string[] args)
        {
            //Erstellung einer Liste mit Strings und Zeitstempel
            List<String> texts = new List<string>() { "Das", "ist", "ein", "Beispiel", "mit Text" };
            String currentTime = DateTime.Now.ToString("HH:mm:ss");
            texts.Add("Erstellt um: " + currentTime);

            //Pfad wo unser .txt-File erstellt und ausgelesen werden soll
            string path = "F:\\Temp\\Testfile.txt";

            //Erstellung unseres StreamWriters
            StreamWriter sw = new StreamWriter(path);

            //Durchlauf jedes Eintrags der Liste und Schreiben in das File
            foreach (string s in texts)
            {
                sw.WriteLine(s);
            }
            sw.Close();

            //Erstellung unseres StreamReaders
            StreamReader sr = new StreamReader(path);

            //Zeilenweises Auslesen und Ausgeben der Textdatei
            while (sr.Peek() != -1)
            {
                Console.WriteLine(sr.ReadLine());
            }
            sr.Close();

            //Kopieren des Textfiles als Backup
            String copyPath = path + "_BKP";
            File.Copy(path, copyPath);
            Console.ReadLine();
        }
    }
}

Mit dem .Net Framework hat man sehr viele Möglichkeiten mit Daten zu arbeiten. Diese umfassen unter anderem das Bearbeiten von Verzeichnissen und Dateien oder deren Transport von A nach B. Gemeinhin wird dieser Datenfluss als Stream bezeichnet. Zwei Methoden die solche Datenströme erzeugen kennst du bereits, nämlich Console.WriteLine und Console.ReadLine.
In diesem Beispiel wollen wir uns den Klassen StreamWriter und StreamReader widmen um zum Beispiel Texte in eine Datei zu schreiben und wieder auszulesen.
Anschließend werden wir ein einfaches Backup davon erstellen.

Zuerst erstellen wir uns eine einfache Liste mit Strings die wir uns später in ein Textfile ablegen lassen wollen. Damit wir später auch wissen wann das passiert ist, fügen wir per DateTime.Now.ToString("HH:mm:ss"); einen Zeitstempel hinzu.
Bei der Pfadangabe müssen wir beachten den Backslash entsprechend zu escapen, dass der Pfad erreichbar ist und wir passende Berechtigungen haben (zB. auf Netzwerkpfade).
Die Erstellung eines neuen StreamWriter-Objektes ist denkbar einfach. Alles was wir dafür brauchen ist unser Dateipfad.
In der Nachfolgenden foreach-Schleife gehen wir nun jeden String der Liste durch und schreiben diesen per WriteLine() in unser Textfile. Alternativ kann man auch die Write()-Methode verwenden um die Strings in einer Zeile zu speichern.
Am Ende des Vorgangs bitte nicht vergessen per Close() den StreamWriter wieder zu schließen um beanspruchte Ressourcen freizugeben.
Der StreamReader funktioniert sehr ähnlich. Bei der Initialisierung benötigen wir ebenfalls nur den Dateipfad. Um während des Auslesens nicht über den eigentlichen Textumfang hinaus zu laufen, prüfen wir mit Peek() ob noch weitere Zeilen im Textfile enthalten sind. Wenn ja, dann geben wir diesen per Console.WriteLine(sr.ReadLine()); Zeilenweise in der Konsole aus.
Auch hier schließen wir den Stream per Close().

Abschließend erstellen wir eine Kopie der eben erstellten Datei. Die statische Klasse File bietet hierführ eine Vielzahl an Methoden. Eine davon ist die Copy()-Methode. Übergeben wir dieser einen Quell- und Zielpfad wird uns prompt eine entsprechende Kopie erstellt.

Operatoren I

  • Code
  • Erklärung
  • Ergebnis


//this -Operator
class Mitarbeiter 
{ 
    private string vorname; 
    private string nachname; 
    // Konstruktor: 
    public Mitarbeiter(string vorname, string nachname) 
    { 
        // Name des Objektes Mitarbeiter wird mit den uebergebenen Parametern befuellt 
        this.vorname = vorname; 
        this.nachname = nachname; 
    } 
} 


// ? -Operator
int? length = cars?.Length; // null wenn cars null ist 
Customer first = cars?[0];  // null wenn cars null ist 
int? count = cars?[0]?.Tires?.Count();  // null wenn cars, das erste car, oder Tires null ist 

// ?? -Operator
// Setze y auf x. Wenn x null ist, dann setze y auf -1
int y = x ?? -1; 

// ?: -Operator (verkuerztes if)
//Bedingung ? Erste_Aweisung(true) : Zweite_Anweisung(false); 
int a = 5; 
int b = 7; 
 
int bigger = (a > b) ? a : b; // b wird zugewiesen 
 
 
// % -Operator (Modulo) 
// Rest einer Division
int a = 5; 
a = a % 3; //(oder auch a %= 3) 
//a=2 

Der .this -Operator
Der .this Operator verweist auf die Verwendung der Variable (oder Methode) des aktuell instanzierten Objektes. Besonders hilfreich bei der Verwendung von Klassenmethoden die Variablen mit dem gleichen Namen wie Klassenvariablen enthalten.

Der ? (??) -Operator
C# bietet mit dem ? -Operator eine bequeme Null-Prüfung. Um eine entsprechende null-reference exception zu vermeiden werden solche Null-Prüfungen eingesetzt.

?: -Operator
Der ?: -Operator bietet eine verkürzte Schreibweise der klassischen if-Bedingung in der Form (Bedingung) ? (Anweisung wenn True) : (Anweisung wenn false);

% -Operator (Modulo)
Das Modulo ist der Rest einer Division (5 / 3 = 1 -> 2 Rest). Einfaches Anwednungsbeispiel: prüfen ob eine Zahl gerade oder ungerade ist.