Skip to content

Latest commit

 

History

History
851 lines (677 loc) · 25.7 KB

File metadata and controls

851 lines (677 loc) · 25.7 KB

Vorlesung Softwareentwicklung - 3 - Elemente der Sprache


Link auf die aktuelle Vorlesung im Versionsmanagementsystem GitHub

https://github.com/liaScript/CsharpCourse/blob/master/03_ElementeII.md

Die interaktive Form ist unter diese Link zu finden -> LiaScript Vorlesung 3


Wie weit sind wir schon gekommen?

c# Schlüsselwörter:

| abstract | as | base |bool | break |byte |
| case | catch | char |checked |class | const | | continue | decimal | default | delegate | do |double | | else | enum | event | explicit | extern |false | | finally | fixed |float | for | foreach | goto | | if | implicit | in |int | interface | internal | | is | lock |long |namespace | new | null | | object | operator | out | override | params | private | | protected | public | readonly | ref | return |sbyte | | sealed |short | sizeof | stackalloc |static |string | | struct | switch | this | throw |true | try | | typeof |uint |ulong |unchecked | unsafe |ushort | | using | virtual |void | volatile | while | |

Auf die Auführung der kontextabhängigen Schlüsselwörter wie where oder ascending wurde hier verzichtet.


Kontrollfragen

1. Welche Funktionalität lässt sich mit dem Schlüsselwort checked abdecken?

[( )] Überwachung von allen arithmetischen Operationen [(X)] Überlaufüberprüfung bei arithmetischen Operationen für ganzzahlige Typen und Konvertierungen [( )] Genauigkeitsüberwachung bei Gleitkommazahlen

2. Hier stehen jetzt Ihre Fragen ...


Boolscher Datentyp und Operatoren

Der bool-Typ umfasst die logischen Werte true and false. Diese sind durch keine cast-Operatoren in numerische Werte und umgekehrt wandelbar!

using System;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            bool x = true;
            int y = 1;
            Console.WriteLine(x == y);
        }
    }
}

@Rextester.eval(@CSharp)

Die Vergleichsoperatoren == und != testen auf Gleichheit oder Ungleichheit für jeden Typ und geben in jedem Fall einen bool Wert zurück. Dabei muss unterschieden werden zwischen Referenztypen (Zeigen beide Variablen auf die gleiche Objektinstanz?) und Wertetypen (Stimmen die spezifischen Werte überein?).

using System;

namespace Rextester
{
    public class Person{
      public string Name;
      public Person (string n) {Name = n;}
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            Person student1 = new Person("Sebastian");
            Person student2 = new Person("Sebastian");
            Console.WriteLine(student1 == student2);
        }
    }
}

@Rextester.eval(@CSharp)

Die Gleichheits- und Vergleichsoperationen ==, !=, >=, > usw. sind auf alle numerischen Typen anwendbar. Für Nutzerspezifische Datentypen können die entsprechenden Operatoren überladen werden.

Logische Operatoren &, &&, |, || und ^ ermöglichen die Verknüpfung von Boolschen Aussagen. Dabei wird zwischen "konditionalen" und "nicht-konditionalen" Operatoren unterschieden. Die erste Gruppe kann auf alle Basisdatentypen angewandt werden die letztgenannte nur auf bool.

using System;

namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
            byte a =  6; // 0110
            byte b = 10; // 1010

            Console.WriteLine(Convert.ToString(a & b, toBase:2));
        }
    }
}

@Rextester.eval(@CSharp)

bool a=true, b=true, c=false;
Console.WriteLine(a || (b && c)); // short-circuit evaluation

// alternativ
Console.WriteLine(a | (b & c));   // keine short-circuit evaluation

Char / String Datentypen und Operatoren

                               {{0-1}}

Type char

Der char Datentyp repräsentiert Unicode Zeichen (vgl. Link) mit einer Breite von 2 Byte.

char Bedeutung Wert
\' Einzelnes Anführungszeichen 0x0027
\\ Backslash 0x0022
\0 Null 0x0000
\n Neue Zeilenenden 0x000A
\t Tabulator 0x0009

Mit \u oder \x lassen sich zudem alle Unicode Zeichen mit einem 4-elementigen Hexadezimalen Code abrufen.

using System;  
namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
          Console.WriteLine('\u2328' + " Unicodeblock Miscellaneous Technical");
          Console.WriteLine('\u2F0C' + " Unicodeblock Kangxi Radicals");
        }
    }
}

@Rextester.eval(@CSharp)

Entsprechend der Datenbreite können char Variablen implizit in short überführt werden. Für andere numerische Typen ist eine explizite Konvertierung notwendig.


{{1-4}} Type string

                               {{1-2}}

Als Referenztyp verweisen string Instanzen auf Folgen von Unicodezeichen abgeschlossen durch ein Null \0. Bei der Interpretation der Steuerzeichen muss hinterfragt werden, ob eine Ausgabe des Zeichens oder eine Realisierung der Steuerzeichenbedeutung gewünscht ist. Dazu wird der verbatim Suffix genutzt.

using System;  
namespace Rextester
{
    public class Program
    {
        public static void Main(string[] args)
        {
          Console.WriteLine("Das ist ein \n Test der \t über mehrere Zeilen geht!");
          Console.WriteLine(@"Das ist ein \n Test der \t über mehrere Zeilen geht!");
        }
    }
}

@Rextester.eval(@CSharp)


                               {{2-3}}

Der Additionsoperator steht für 2 string Variablen bzw. 1 string und eine andere Variable als Verknüpfungsoperator (sofern für den zweiten Operanden die Methode toString() implementiert ist) bereit.

using System;  
namespace Rextester
{
    public class Person{
      public string Name;
      public Person (string n) {Name = n;}
    }

    public class Program
    {
        public static void Main(string[] args)
        {
          Console.WriteLine("String + String = " + "StringString" );
          Console.WriteLine("String + Zahl 5 = " + 5); // Implizites .ToString()

          Person ich = new Person ("Sebastian");
          Console.WriteLine("Wer ist das? " + ich);
        }
    }
}

@Rextester.eval(@CSharp)

Der Gebrauch des + Operators im Zusammenhang mit string Daten ist nicht effektiv. eine bessere Performanz bietet System.Text.StringBuilder.

In der nächsten Vorlesung werden wir uns explizit mit den Konzepten der Ausgabe und entsprechend den Methoden der String Generierung beschäftigen.


Enumerations

                               {{0-1}}

Enumerationstypen erlauben die Auswahl aus einer Aufstellung von Konstanten, die als Enumeratorlisten bezeichnet wird. Was passiert intern? Die Konstanten werden auf einen ganzzahligen Typ außer char gemappt. Der Standardtyp von Enumerationselementen ist int. Um eine Enumeration eines anderen ganzzahligen Typs, z. B. byte zu deklarieren, setzen Sie einen Doppelpunkt hinter dem Bezeichner, auf den der Typ folgt.

using System;  

namespace Rextester
{
  public class Program
  {
    enum Day {Sat, Sun, Mon, Tue, Wed, Thu, Fri};

    public static void Main(string[] args)
    {
      Day startingDay = Day.Wed;
      Console.WriteLine(startingDay);
    }
  }
}

@Rextester.eval(@CSharp)


                               {{1-2}}

Dabei schließen sich die Instanzen nicht gegenseitig aus, mit einem entsprechenden Attribut können wir auch Mehrfachbelegungen realisieren.

// https://docs.microsoft.com/de-de/dotnet/api/system.flagsattribute?view=netframework-4.7.2

using System;  

namespace Rextester
{
  public class Program
  {
    [FlagsAttribute] // <- Specifisches Enum Attribut
    enum MultiHue : short
    {
       None = 0, Black = 1, Red = 2, Green = 4, Blue = 8
    };

    public static void Main(string[] args)
    {
       Console.WriteLine(
            "\nAll possible combinations of values with FlagsAttribute:");
       for( int val = 0; val <= 16; val++ )
          Console.WriteLine( "{0,3} - {1:G}", val, (MultiHue)val);
    }
  }
}

@Rextester.eval(@CSharp)


Arrays

                               {{0-1}}

Arrays sind potentiell multidimensionale Container beliebiger Daten, also auch von Arrays und haben folgende Eigenschaften:

  • Ein Array kann eindimensional, mehrdimensional oder verzweigt sein.
  • Die Größe innerhalb der Dimensionen eines Arrays wird festgelegt, wenn die Arrayinstanz erstellt wird. Eine Anpassung zur Lebensdauer ist nicht vorgesehen.
  • Numerische Arrayelemente sind standardmäßig auf 0 (null) festgelegt, Verweiselemente auf NULL.
  • Arrays sind nullbasiert: Der Index eines Arrays mit n Elementen beginnt bei 0 und endet bei n-1.
  • Arraytypen sind Referenztypen, die IEnumerable und IEnumerable implementieren, können also mit foreach iteriert werden.

                               {{1-2}}

Eindimensionale Arrays

Eindimensionale Arrays werden über das Format

<typ>[] name = new <typ>[<anzahl>];

deklariert. Die spezifische Größenangabe kann entfallen, wenn mit der Deklaration auch die Initialierung erfolgt.

<typ>[] name = new <typ>[] {<eintrag_0>, <eintrag_1>, <eintrag_2>};
using System;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
      int [] intArray = new int [5];
      short [] shortArray = new short[] { 1, 3, 5, 7, 9 };
      for (int i = 0; i < 3; i++){
        Console.Write("{0, 3}", intArray[i]);
      }
      Console.WriteLine("");
      string sentence = "Das ist eine Sammlung von Worten";
      string [] stringArray = sentence.Split();

      foreach(string i in stringArray){
        Console.Write("{0, -9}", i);
      }
    }
  }
}

@Rextester.eval(@CSharp)


                               {{2-3}}

Achtung Die unterschiedliche Initialisierung von Wert- und Referenztypen generiert ggf. Fehler!

Erzeugung eines Arrays von structs - Wertetypen

public struct Point {public int X, Y;}
....
Point [] pointcloud = new Point[100];
int x = pointcloud[99].X                    // x = 0

Erzeugung eines Arrays von Klasseninstanzen - Referenztypen

public class Point {public int X, Y;}
....
Point [] pointcloud = new Point[100];
int x = pointcloud[99].X                    // Runtime Error, Null-Referenz!

                               {{3-4}}

Mehrdimensionale Arrays

C# unterscheidet zwei Typen mehrdimensionaler Arrays, die sich bei der Initalisierung und Indizierung unterschiedlich verhalten.

  Rechteckige Arrays
                      ┏━━━━━┯━━━━━┯━━━━━┯━━━━━┓
  a[zeile, Spalte] ──>┃[0,0]│[0,1]│[0,2]│[0,3]┃
                      ┠─────┼─────┼─────┼─────┨    
                      ┃[1,0]│[1,1]│[1,2]│[1,3]┃
                      ┗━━━━━┷━━━━━┷━━━━━┷━━━━━┛


  Ausgefranste Arrays

                    ┏━━━┓       ┏━━━━━━━┯━━━━━━━┯━━━━━━━┯━━━━━━━┓
   a[index] ──>     ┃[0]┃ ──>   ┃[0],[0]│[0],[1]│[0],[2]│[0],[3]┃
                    ┠───┨       ┣━━━━━━━┿━━━━━━━┿━━━━━━━┷━━━━━━━┛
                    ┃[1]┃       ┃[0],[0]│[0],[0]┃
                    ┠───┨       ┣━━━━━━━┿━━━━━━━┿━━━━━━━┓
                    ┃[2]┃       ┃[0],[0]│[0],[1]│[0],[1]┃
                    ┗━━━┛       ┗━━━━━━━┷━━━━━━━┷━━━━━━━┛
int[,] =  rectangularMatrix =
{
  {1,2,3},
  {0,1,2},
  {0,0,1}
};

int [][] = jaggedMatrix ={
    new int[] {1,2,3},
    new int[] {0,1,2},
    new int[] {0,0,1}
};

Implizit typisierte Variablen

C# erlaubt bei den lokalen Variablen eine Definition ohne der expliziten Angabe des Datentyps. Die Variablen werden in diesem Fall mit dem Schlüsselwort var definiert, der Typ ergibt sich infolge der Auswertung des Ausdrucks auf der rechten Seite der Initialisierungsanweisung zur Compilierzeit.

var i = 10; // i compiled as an int
var s = "untypisch"; // s is compiled as a string
var a = new[] {0, 1, 2}; // a is compiled as int[]

var-Variablen sind trotzdem typisierte Variablen, nur der Typ wird vom Compiler zugewissen.

Vielfach werden var-Variablen im Initialisierungteil von for- und foreach- Anweisungen bzw. in der using-Anweisung verwendet. Eine wesentliche Rolle spielen sie bei der Verwendung von anonymen Typen. Aber Vorsicht: var kann nur mit lokalen Variablen verwendet werden.

using System;
using System.Collections.Generic;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
      //int num = 123;
      //string str = "asdf";
      //Dictionary<int, string> dict = new Dictionary<int, string>();

      var num = 123;
      var str = "asdf";
      var dict = new Dictionary<int, string>();

      Console.WriteLine("{0}, {1}, {2}", num.GetType(), str.GetType(), dict.GetType());

    }
  }
}

@Rextester.eval(@CSharp)

Weitere Infos https://docs.microsoft.com/de-de/dotnet/csharp/programming-guide/classes-and-structs/implicitly-typed-local-variables

2. Anweisungen

Anweisungen setzen sich zusammen aus Zuweisungen, Methodenaufrufen, Verzweigungen Sprunganweisungen und Anweisungen zur Fehlerbehandlung.

ISO 9126 WikiFlow

Verzweigungen

                               {{0-1}}

if

Verzweigungen in C# sind allein aufgrund von boolschen Ausdrücken realisiert. Eine implizite Typwandlung wie in C if (value) ist nicht vorgesehen.

if (BooleanExpression) Statement else Statement

Warum brauche sollte ich in jedem Fall Klammern um Anweisungen setzen, gerade wenn diese nur ein Zeile umfasst?

using System;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
        bool A = true, B = false;
        if (A)
          if (B)
            Console.WriteLine("Fall 1");  // A & B
          else
            Console.WriteLine("Fall 2");  // A & not B
    }
  }
}

@Rextester.eval(@CSharp)

Merke: Das setzen der Klammern steigert die Lesbarkeit ...

... nur bei langen else if Reihen kann drauf verzichtet werden.


                               {{1-2}}

switch

Die switch-Anweisung ist eine Mehrfachverzweigung. Sie umfasst einen Ausdruck und mehrere Anweisungsfolgen, die durch case eingeleitet werden.

Anders als bei vielen anderen Sprachen erlaubt C# switch-Verzweigungen anhand auch von strings (zusätzlich zu allen Ganzzahl-Typen, bool, char, enums). Es fehlt hier aber die Möglichkeit sogenannte Fall Through durch das Weglassen von break-Anweisungen zu realisieren. Jeder switch muss mit einem break, return, throw, continue oder goto beendet werden. Interessant ist die Möglichkeit auf case: null zu testen!

using System;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
      string day = "Sonntag";
      string output;
      switch (day){
        case "Montag": case "Dienstag":
        case "Mittwoch": case "Donnerstag": case "Freitag":
          output = "Wochentag";
          break;
        case "Samstag": case "Sonntag":
          output = "Wochenende";
          break;
        default:
          output = "Kein Wochentag!";
          break;
      }
      Console.WriteLine(output);
    }
  }
}

@Rextester.eval(@CSharp)

C# 7.0 führt darüber hinaus das pattern matching mit switch ein. Damit werden komplexe Typ und Werte-Prüfungen innerhalb der case Statements möglich.

public static double ComputeArea_Version(object shape)
{
    switch (shape)
    {
        case Square s when s.Side == 0:
        case Circle c when c.Radius == 0:
        case Triangle t when t.Base == 0 || t.Height == 0:
        case Rectangle r when r.Length == 0 || r.Height == 0:
            return 0;

        case Square s:
            return s.Side * s.Side;
        case Circle c:
            return c.Radius * c.Radius * Math.PI;
        case Triangle t:
            return t.Base * t.Height / 2;
        case Rectangle r:
            return r.Length * r.Height;
    }
}

Codebeispiel aus MSDoku


###Schleifen

Eine Schleife (auch „Wiederholung“ oder englisch loop) ist eine Kontrollstruktur in Programmiersprachen. Sie wiederholt einen Anweisungs-Block – den sogenannten Schleifenrumpf oder Schleifenkörper –, solange die Schleifenbedingung als Laufbedingung gültig bleibt bzw. als Abbruchbedingung nicht eintritt. Schleifen, deren Schleifenbedingung immer zur Fortsetzung führt oder die keine Schleifenbedingung haben, sind Endlosschleifen.

Zählschleife - for

for (initializer; condition; iterator)
    body

Üblich sind für alle drei Komponenten einzelne Anweisungen. Das erhöht die Lesbarkeit, gleichzeitig können aber auch komplexere Anweisungen integriert werden.

using System;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
      for (int i = 0, j = 10;
           i<10 && j>5;
           Console.WriteLine("Start: i={0}, j={1}", i, j), i++, j--)
        {
            //empty
        }
    }
  }
}

@Rextester.eval(@CSharp)

Kopf- Fußgesteuerte schleife - while/do while

Eine while-Schleife führt eine Anweisung oder einen Anweisungsblock so lange aus, wie ein angegebener boolescher Ausdruck gültig ist. Da der Ausdruck vor jeder Ausführung der Schleife ausgewertet wird, wird eine while-Schleife entweder nie oder mehrmals ausgeführt. Dies unterscheidet sich von der do-Schleife, die ein oder mehrmals ausgeführt wird.

Iteration - foreach

Als alternative Möglichkeit zum Durchlaufen von Containern, die IEnumerable implementieren bietet sich die Iteration mit foreach an. Dabei werden alle Elemente nacheinander aufgerufen, ohne dass eine Laufvariable nötig ist.

using System;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
      int [] array = new int [] {1,2,3,4,5,6};
      foreach (int entry in array){
          Console.Write("{0} ", entry);
      }
    }
  }
}

@Rextester.eval(@CSharp)

Sprünge

Während bestimmte Positionen im Code adressiert, lassen sich mit break Schleifen beenden, dient continue der Unterbrechung des aktuellen Blockes.

Sprunganweisung Wirkung
break beendet die Ausführung der nächsten einschließenden Schleife oder switch-Anweisung
continue realisiert einen Sprung in die nächste Iteration der einschließenden Schleife
goto <label> Sprung an eine Stelle im Code, er durch das Label markiert ist
return beendet die Ausführung der Methode, in der sie angezeigt wird und gibt den optional nachfolgenden Wert zurücksetzen
using System;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
      int dummy = 0;
      for (int y = 0; y < 10; y++)
      {
        for (int x = 0; x < 10; x++)
        {
          if (x == 5 && y == 5)
          {
            goto Outer;
          }
        }
        dummy++;
      }
      Outer:
          Console.WriteLine(dummy);
    }
  }
}

@Rextester.eval(@CSharp)

Vgl. Links zur Diskussion um goto auf https://de.wikipedia.org/wiki/Sprunganweisung

3. Beispiel der Woche ...

MonteCaroloPI WikiMonteCarlo

Betrachtet wird ein Einheitsquadrat mit Einheitsviertelkreis, indem $n$ zufällige Punkte erzeugt werden. Dabei verhält sich die Anzahl Punkte im Viertelkreis zur Gesamtanzahl Punkte genauso wie der Flächeninhalt des Viertelkreis zur Quadratfläche:

$$\frac{Punkte_{in}}{Punkte_{all}} = \frac{A_{Kreisausschnitt}}{Quadratfläche} = \frac{\frac{1}{4}\pi r^2}{r^2} = \frac{1}{4} \pi$$

using System;

namespace Rextester
{
  public class Program
  {
    public static void Main(string[] args)
    {
      var random = new Random();
      double x, y, dist;
      int inside = 0, outside = 0;
      for (int i=0; i<100000; i++){
        x = random.NextDouble();
        y = random.NextDouble();
        dist = Math.Sqrt(x*x + y * y);
        if (dist > 1) outside ++;
        else inside++;
      }
      Console.WriteLine("{0:F}", 4 * (float)inside/(inside + outside));
    }
  }
}

@Rextester.eval(@CSharp)

Anhang

Referenzen

[MSDoku] C# Dokumentation, "Pattern Matching", Link

[WikiMonteCarlo] ZUM-Wiki, "Monte Carlo Simulation" Autor "Springob", Link

[WikiFlow] Wikipedia, "Flussdiagramm" Autor "Erik Streb", Link

Autoren

Sebastian Zug, André Dietrich