Inhaltsverzeichnis         

1 VB.NET

1.1 BASIC war mal eine Anfängersprache ...

BASIC ist ein Akronym (Beginner’s All-purpose Symbolic Instruction Code = Allzweck-Symbolbefehlssprache für Anfänger) und bezeichnet eine Programmiersprache, die Mitte der 60-er von den US- Amerikanern John Kemeny und Thomas Kurtz entwickelt wurde. Sie diente ursprünglich für Schulungszwecke.

Ursprünglich war BASIC als Interpreter Sprache konzipiert. Ein geringer Umfang an Befehlen, die Invarianz gegenüber der Groß/Kleinschreibung und die Interaktivität durch den Interpreter führten dazu, dass BASIC insbesondere unter Anfängern beliebt wurde.

1.2 VB.NET Compiler

Im folgenden werden die Stufen der Übersetzung eines VB.NET- Programms dargestellt:

1.2.1 Übersetzen auf der Kommandozeile

Im Folgenden wird ein einfaches Programm erstellt, welches auf der Kommandozeile den String "Hallo Welt" ausgibt. Anhand des Programmes wird die Kompilation in MSIL sowie das Kompilat selbst untersucht.

1.2.1.1 Beispiel Hallo Welt

Legen sie eine Datei namens Hallo.vb an.

=> Quelltexte werden in Unicode- Dateien mit der Endung vb gespeichert.

' VB.NET Kennenlernen
' Kommentare sind einzeilig und werden mit einem Hochkomma (') eingeleitet
' Alle Prozeduren und Funktionen müssen in einem Modul-
' oder Klassenblock eingeschlossen werden
Module m1

    ' Die Prozedur Main hat eine besondere Bedeutung: Beim Programmstart 
    ' wird vom Lader des Betriebssystems an diese die Kontrolle übergeben
    Sub Main() 

        ' Hier wird ein Unterprogramm aus der Bibliothek System.dll aufgerufen
        ' Mittels des Unterprogramms Console.WriteLine erfolgt die Ausgabe
        ' der Zeichenkette "Hallo Maja" auf der Kommandozeile
        System.Console.WriteLine("Hallo Maja")

    End Sub

End Module

1.2.1.2 Übersetzen

Das Beispielprogramm kann auf der Kommandozeile (Visual- Studio- Tools/Command Prompt) durch folgenden Befehl kompiliert werden:

c:\vb-kurs\vbc hallo.vb /r: System.dll

Die Option /r weist den Compiler an, Einsprungpunkte für Unterprogramme aus der Bibliothek System.dll anzulegen.

1.2.2 Präprozessordirektiven

Mittels Präprozessordirektiven können Abschnitte von der Kompilation ausgeschlossen werden.

1.2.2.1 # const

Mit #const kann eine Konstante definiert werden, die in Präprozessorblöcken für die bedingte Kompilation ausgewertet wird. Die Konstanten müssen als erste Anweisungen in einer Quelltextdatei definiert werden.

#const XX_GRUSS_MAJA = True
Module m1

    Sub Main() 
       :
   End Sub
End Module

Hier wird eine Konstante namens XX_GRUSS_MAJA deklariert.

Nicht deklarierte Konstanten in Präprozessorblöcken werden durch Nothing ersetzt und stellen damit den booleschen Wert false dar.

1.2.2.2 #if ... then #else

In Abhängigkeit von bedingten Konstanten können Abschnitte im Quelltext von der Kompilation ausgeschlossen werden oder nicht:

#const XX_GRUSS_MAJA = True
Module m1

    Sub Main() 

#if XX_GRUSS_MAJA then
        System.Console.WriteLine("Hallo Maja")
#else
        System.Console.WriteLine("Hallo Marina")
#endif

   End Sub
End Module

In diesem Beispiel wird die Zeile zwischen #else ... #endif nicht kompiliert. Wenn die erste Zeile (#const ...) auskommentiert wird, dann erfolgt keine Kompilation für die Zeile zwischen #if ... #else.

1.2.2.3 # region

Dient zur logischen Gliederung des Quelltextes. Die IDE kann Regionen wahlweise auf- und zuklappen. Bsp.:

#region "mm"
  sub p1
   ...
  end sub
  sub p2
   ...
  end sub
#end region

1.2.3 Testaugaben mittels Debug und Trace Klassen

Bei der Entwicklung von Programmen muss der Programmierer oft das Verhalten zur Laufzeit überwachen. Dazu macht er hilfsweise Ausgaben von aktuellen Variablenwerten, dem Ergebnis von Berechnungen etc.. Dazu bietet das .NET Framework ein ideales Instrument, die Debug- und Trace Klasse.

Die Debug- Klasse bietet Unterprogramme zur testweisen Ausgabe und zum testweise prüfen von Werten an:

' Testweise Ausgabe des Wertes x im Direkt/Ausgabefenster von VS2012
Debug.WriteLine("x= " & x)

' Testweise überprüfung, ob x größer 0 ist. Wenn nicht,
' dann wird eine Ausnahme ausgelöst
Debug.Assert(x > 0)

Wie die Präprozessorbefehle können alle Unterprogrammaufrufe von Debug über einen Compilerschalter wahlweise aus dem Quelltext vor der eigentlichen Übersetzung entfernt werden. Die Schalter sind die Debug- und Trace- Preprozessorkonstanten.

1.3 Grundlegende Sprachelemente

1.3.1 Blöcke

Die elementarste Struktur aller Programmiersprachen sind Blöcke. Ein Block ist ein zusammenhängendes Stück Text (Programmcode), das durch spezielle Schlüsselworte abgegrenzt wird.

Blockbeginn Schlüsselwort
  Programmtext
Blockende Schlüsselwort

Im Folgenden einige wichtige Blöcke in VBA als Beispiel:

Schlüsselwörter

Bezeichnung

Namespace <Blockname>

End Namespace

Definiert einen Bereich, innerhalb dessen Namen eindeutig sein müssen. Außerhalb des Blocks wird jeder Name durch das Blocknamenspräfix eindeutig.

Modul <Modulname>
   ' Anweisungen
   …
End Module

Container, die Functions, Sub's und Variablen aufnehmen, die zu einem Aufgabenkomplex gehören oder in einem sachlichen Zusammenhang stehen.

Z.B. können die Funktionen, die bei der Implementierung der Teilschritte des Primzahlscanners entstanden (IstPrimzahl(z), TesteAufTeiler(z, t) und AllePrimzahlenIn(a, b)) in einem Modulblock mit dem Namen Primzahlscanner zusammengefasst werden.

Sub <Unterprogrammname>
   ' Anweisungen
   ...
End Sub

Unterprogrammblock: Schließt Liste von Anweisungen ein, die über einen Namen geladen und ausgeführt werden können

IF <Bedingung> Then
  ' Anweisungen
  …
End IF

Bedingter Anweisungsblock: Schließt Liste von Anweisungen ein, die nur ausgeführt werden, wenn die Bedingung erfüllt ist.

Class <Klassenname>
  ' Anweisungen
  …
End Class

Klassenblock. Fasst alle Sub, Functions und Properties zusammen, die Objekte einer Klasse besitzen.

1.3.1.1 .NET Einschlussdogma

VB.NET ist Bestandteil von .NET und unterliegt damit der CLS (Common Language Spezification). Die verlangt, das jede .NET Sprache streng objektorientiert ist. Damit muss jede Variable und jede Prozedur in einem Klassen- oder Modulblock definiert werden. "Freistehende" Variablen sind verboten.

Namespace N1

  ' Falsch ! Sub wurde außerhalb eines Class oder Module- Block deklariert
  Sub TueWas()
    …
  End Sub

  Module M1

     ' richtig ! Sub wurde innerhalb eines Class oder Module- Block deklariert
     Sub TueWas()
       …
     End Sub

  End Module

End Namespace

1.3.1.2 Verschachtlung

Die Anweisungslisten innerhalb von Blöcken können wieder in Blöcke eingeschlossen werden, so dass eine Baumstruktur entsteht. Man spricht von Verschachtlung.

Allgemein

Beispiel

Beginn Block1 
  ' Anweisungen
  ...
  Beginn Block2
     ' Anweisungen
     ...
  End Block2
  ' Anweisungen 
  ...   
End Block1
Sub Hauptprogramm

   A = 5
   B = 4

   IF A > B Then
     Debug.Print "A ist größer als B"
   End IF  

End Sub

1.3.1.3 Namespaces

Namespaces dienen zur Abgrenzung der Namensmengen zwischen verschiedenen Teilen von Bibliotheken und Programmen.

Namespace outer 

  Namespace inner 

     Modul MyMod
        ' Deklaration des Namens x im Namespace outer.inner.MyMod
        public x as Integer
     End Modul

  End Namespace

  Modul MyMod
     ' Deklaration des Namens x im Namespace outer.MyMod
     public x as Integer
  End Modul

End Namespace

// Zugriff auf x 
outer.inner.MyMod.x = 99;
1.3.1.3.1 Imports- Direktive

Der Zugriff auf Typen und Funktionen , die in tief verschachtelten Namensräumen definiert sind, kann zu einem sehr unübersichtlichen Code führen. Durch die Imports- Direktive kann am Anfang eine Namespace- Präfix vereinbart werden, das der Compiler auf nicht auflösbare Namen anwenden kann.

Imports Namespace
Imports Alias = Namespace

1.3.2 Ausdrücke

Ein Ausdruck ist in VB.NET ein Konstrukt aus Namen, Werten, Operatoren und Funktionsaufrufen, das zu einem Wert ausgewertet werden kann. Die Auswertung wird als Evaluierung bezeichnet.

Beispiele:

3             // wird zu 3 ausgewertet
3 + 4         // wird zu 7 ausgewertet
(3 + 4) * 2   // wird zu 14 ausgewertet
D             // Auswertung liefert den Inhalt der Variable D
Math.Pi * D   // wird zum Kreisumfang ausgewertet, wenn in über Variable D
              // der Durchmesser bereitgestellt wird.

1.3.2.1 Bedingte Ausdrücke

Ein bedingter Ausdruck ist eine spezielle VB.NET- Funktion mit drei Parametern:

Ausgangswert = IIF(Bedingung, ExprA, ExprB)

Zuerst wird die Bedingung evaluiert. Ergibt sie den Wert true, dann wird ExprA evaluiert und deren Wert entspricht dem Ausgangswert. Sonst entspricht der Evaluierte Wert von ExprB dem Ausganswert.

1.3.2.2 Funktionen

Eine Funktion bildet eine Liste von Eingangswerten aus einen Ausgangswert ab:

          E1 --+
               |
Eingänge  E2 --+-Funktion_f-> Ausgangswert
          …    |
          En --+

Ein funktionale Abbildung wird in VB.NET durch spezielle Ausdrücke, den Funktionsaufrufen implementiert:

Funktionswert = Funktion_f (E1, E1, …, En)
                   |      |_____________|
                   |             |
               Funktions-  Parameterliste
               name

In VB.NET gibt es bereits viele eingebaute Funktionen. Beispiele:

' Eingabaute Funktionen
Sub TestBuildInFunctions()

    Dim y As Double
    
    ' Sinusfunktion (Mathe, Trigonometrie)
    y = Math.Sin(Application.Pi / 2)
    Debug.Assert (y = 1.0)
    
    Dim anzZeichen As String
    
    ' Zeichenkettenfunktion. Eingegebener Text wird auf die Anzahl
    ' der Zeichen des Textes abgebildet
    anzZeichen = Len("Hallo Welt")
    Debug.Assert (anzZeichen = 10)
    
    Dim datum As Date
    
    ' Datumsfunktion: Die eingegebenen Werte (Jahr, Monat, Tag) werden
    ' auf einen VBA- Datumswert abgebildet
    datum = DateSerial(2013, 11, 29)
    Debug.Assert (datum = #11/29/2013#)    

End Sub

1.3.3 Anweisungen

Ein Programmtext besteht in VB.NET aus Anweisungen. Eine Anweisung ist ein ausführbarer Befehl. Befehle können elementar oder komplex sein.

Elementare Befehle sind direkt ausführbar wie z.B. einfache Sprungbefehle:

Goto SPRUNGMARKE 

Komplexe Befehle setzen sich aus Speicherzugriffsbefehlen, Operationen und Zuweisungen zusammen.

A = 3 * (B + 99)

Anweisungen werden entweder durch ein Zeilenumbruch - Zeichen oder durch : getrennt.

Abschluss mit Zeilenumbruch

Abschluss mit :

Kombiniert

Blockbeginn Schlüsselwort  
  Anweisung_1
  Anweisung_2
  …
  Anweisung_N
Blockende Schlüsselwort
Blockbeginn Schlüsselwort  
  Anweisung_1:Anweisung_2  
Blockende Schlüsselwort
Blockbeginn Schlüsselwort  
  Anweisung_1:Anweisung_2
  …
  Anweisung_N
Blockende Schlüsselwort

1.3.3.1 Kommentar - Anweisung

Mit der Kommentar - Anweisung ' kann Text bis zum Zeilenumbruch von der Übersetzung ausgeschlossen werden. Dies kann man nutzen, um bei der Entwicklung temporär Code abzuschalten oder Anmerkungen in den Programmkode einzufügen. Im Folgenden einige Einsatzbeispiele für Kommentare:

' Eine Kommentarzeile

' Eine temporär abgeschaltete Anweisung
' A = 3 * B

B = 5 * C ' Ein Kommentar im Anschluss an eine Anweisung

1.3.3.2 Zuweisung

Eine Zuweisung ist eine spezielle VB.NET- Anweisung der Form:

Variable = Ausdruck

Sie entspricht einem Kopierbefehl, wobei der evaluierte Ausdruck rechts neben dem Gleichheitszeichen an den benannten Speicherplatz (Variable) links neben den Gleichheitszeichen kopiert.

1.3.4 Literale

Literale gehören zu den Grundbausteinen einer Programmiersprache und bezeichnen die Vereinigung der Mengen aller

Standard in VB.NET ist die Darstellung von Zahlenwerten im dezimalen Zahlensystem (Basis 10). Der Wert von logischen Aussagen kann durch die beiden Elemente true oder false ausgedrückt werden. Es gibt Literale für Datumswerte, die auf der US- amerikanischen Notation basiert.

Menge

Beispiel

Festkommawerte

123

Festkommawert, hexadezimal

&HAF

Festkommawert, oktal

&O73

Festkommawert, dezimal

123.4D

Gleitkommawerte

123.4

Gleitkommawerte in Exponentialschreibweise

1.23e4

negative Festkomma und Gleitkommawerte

-123, -123.45

Zeichen

"A"C

Zeichenketten

"Hallo"

Wahrheitswerte

true oder false

Datumsangaben (amerikanisches Format MM/DD/YYYY)

#02/13/2004#

Wert einer leere Referenzvariable

Nothing

1.3.4.1 Spezialwert Nothing

Nothing ist ein spezieller Wert, der jeder Variablen zugewiesen werden kann.

Zuweisung an Variable vom

bewirkt

Wertetyp

Die Variable wird auf ihren Standardwert zurückgesetzt (Strings z.B. auf leere Zeichenkette)

Referenztyp

Bei Objekt variablen verweisen diese nicht mehr auf das Objekt. Das Objekt wird jedeoch erst gelöscht, wenn die Garbage Collection keine Verweise auf das Objekt vorfindet.

1.3.5 Operatoren

Operatoren sind grundlegende Abbildungen von Mengen in sich selbst. Beispielsweise ist die Addition zweier Festkommazahlen ein Operator.

Eine Anweisung kann mehrere Operatoren enthalten wie:

A = 3 * (B + 99)

In diesem Falle muss bei der Ausführung der Anweisung über die Reihenfolge entschieden werden, in der die Operatoren anzuwenden sind. Basis dafür ist die Priorität der Operatoren. Operatoren mit höherer Priorität werden vor Operatoren mit niedrigerer Priorität ausgeführt.

Im folgenden die Liste aller Operatoren von VB.NET:

Priorität

Operator

Beschreibung

höchste

( )

Klammern


^

Potenzieren


-

negatives Vorzeichen


* /

Multiplikation und Division


\

Integer Division


Mod

Modulo bzw. Rest aus Division, z.B. 7 Mod 3 = 1 denn 2*3 + Rest 1 = 7


+ -

Addition und Subtraktion


< > =

kleiner als, größer als, gleich


<> <= >= Like Is TypeOf

ungleich, kleiner gleich, größer gleich


Not

logisches NICHT (Negation)


And Or

logisches UND, logisches ODER

niedrigste

Xor Equ

logisches Exlisiv ODER, Äquivalenz

1.3.6 Variablen

Variablen sind benannte Speicherplätze. In die Speicherplätze wird geschrieben oder aus ihnen wird gelesen, indem auf diese der Zuweisungsoperator = mit einem Wert angewendet wird:




' Speichern des Wertes 3.14 in meineVariable
meineVariable = 3.14

' Lesen eines Wertes aus meinerVariable und speichern in deinerVariable
deineVariable = meineVariable

1.3.6.1 Regeln für Variablennamen

Für die Bezeichnung von Variablen gelten in C# wie in vielen anderen Programmiersprachen auch Einschränkungen.

  1. Erste Zeichen muss ein Buchstabe sein:

    Richtig: A1

    Falsch: 1A

  2. Der Name darf keine Leerraumzeichen enthalten

    Richtig: neuerMitarbeiter

    Falsch: neuer Mitarbeiter

  3. Es sind nur Buchstaben, Zahlen und der Unterstrich _ als Zeichen erlaubt

    Richtig: Preis_in_Euro

    Falsch: Preis_in_€

Der VB.NET Compiler verarbeitet Unicode- Quelltextdateien. Damit sind auch Variablennamen mit z.B. Umlauten zulässig

Dim akt_Ölpreis_in_Dollar as Double = 100.0;

Man sollte jedoch bedenken, dass in internationalen Programmierteams Mitglieder, die keine deutsche Tastatur besitzen, bei der Pflege solcher Quelltexte unnötigen Aufwand haben.

1.3.6.2 Deklaration in VB.NET

Die im Programm verwendeten Variablen werden entweder implizit oder explizit definiert.

1.3.6.2.1 Implizite Deklaration

Implizite Deklaration bedeutet, der Programmierer weist erstmalig einem Namen einen Wert zu. Der VB.NET- Compiler leitet dabei aus dem zugewiesenen Wert ab, wie viel Speicherplatz benötigt wird.

' implizite Deklaration
meineVariable = 0

Die implizite Deklaration ist ein Relikt aus den Anfängen der Sprache BASIC. Wird sie verwendet, dann können sich viele Fehler in das Programm einschleichen, die erst zur Laufzeit entdeckt werden. Beispiel

' impizite Deklaration von Variablen
meineVariable = 0
deineVariable = 0

'…

' Fehler ! Buchstabendreher führt zum impliziten Deklarieren einer neuen Variable
meineVariabel = deineVariable + 1

' Ausgegeben wird im Direktfenster "Wert von meineVariable: 0" !
Debug.Print "Wert von meineVariable: " & meineVariable
1.3.6.2.2 Explizite Deklaration

Mit der expliziten Deklaration werden einige Nachteile der impliziten Deklaration von Variablen überwunden. Bei der expliziten Deklaration muss der zu verwendende Name mittels einer Dim- Anweisung festgelegt werden.

  Dim Variablenname 
1.3.6.2.3 Strikte Deklaration

Eine explizite Deklaration kann noch genauer werden, indem zusätzlich der Datentyp einer Variable vereinbart wird:

  Dim Variablenname As Datentyp

Der Datentyp steht für eine Klasse von Daten. Beispielsweise verbirgt sich hinter dem Datentyp Integer die Menge aller ganzen Zahlen im Intervall [-2147483648, 2147483647]. Der Datentyp bestimmt, wie die Informationen als Dualzahlen im Speicher zu verschlüsseln sind, und wie viel Speicherplatz für die Variable benötigt wird. So werden Variablen vom Typ Integer vom Dezimal- ins Dualsystem konvertiert, und für sie ein Speicherplatz von 32bit = 4Byte reserviert.

Strikt deklarierte Variablen überwacht der Compiler genauer. Wird versucht, z.B. einer Variable einen Wert zuzuweisen, der inkompatibel mit ihrem Datentyp ist, dann meldet der Compiler einen Fehler:

Dim x as Integer
x = "Hallo Welt"  ' Compiler meldet Fehler
1.3.6.2.4 Explizite und stricte Deklaration erzwingen mittels Option Explicit und Option Strict

Mittels folgender Anweisung an den Compiler kann die explizite Deklaration aller Variablen erzwungen werden:

Option Explicit
Option Strict

Erzwingen bedeutet, dass der Compiler Fehler meldet, wenn ein Variablenname verwendet wird, ohne das er vorher explizit und strikt deklariert wurde.

Die Anweisung Option Explicit muss als erste Zeile in einer vb- Quellkodedatei notiert werden.

In Visual Studio 2012 können diese Optionen für alle VB- Projekte eingestellt werden unter

Extras/Optionen/Projekte und Projektmappen/VB Standard
1.3.6.2.5 Initialisierung

Variablen werden in VB.NET explizit oder implizit deklariert.

Bei der impliziten Initialisierung wird der Variable automatisch vom Compiler der Wert Nothing zugewiesen. Im Falle einer Variable vom Typ Integer bewirkt dies, das die Variable den Wert 0 erhält.

' Variable wird implizit mit dem Wert 0 initialisiert
Dim x as Integer

Die explizite Initialisierung besteht in der Zuweisung eines Anfangswertes an die Variable in der Deklaration

' Explizite Initialisierung (allgemein) ...
Dim <Variablenname> as <Datentyp> = <Initialwert>

' … und im Beispiel
Dim x as Integer = 99
1.3.6.2.6 Typinferenz (ab .NET 3.5)

Achtung: Das Folgende Merkmal ist nur bei OPTION INFER ON verfügbar !

Mit Typinferenz wird ein Verfahren bezeichnet, das aus dem Wert, mit dem eine Variable initialisiert wird, den Typ für die Deklaration der Variable ableitet.

Typinferenz wurde im Zusammenhang mit LINQ eingeführt, da der Datentyp des Ergebnisses einer LINQ- Abfrage sich häufig erst aus der Abfrage ergibt.

Typinferenz ist nicht mit dem Verhalten bei Option Strict Off gleichzusetzen, da bei der Typinferenz der Typ nachträglich nicht geändert werden kann !

' Typinferenz: Deklaration ohne Typspezifikation-> Typ wird aus dem zugewiesenen Wert bestimmt
Dim i = 0
Dim str = "Hallo Welt"

' i ist nach wie vor streng typisiert
i = "Hallo Welt"    ' => Fehler, da i vom Typ int

1.3.6.3 Elementare Datentypen (Wertetypen) in VB.NET

Der Datentyp steht für eine Klasse von Daten. Beispielsweise verbirgt sich hinter dem Datentyp Integer die Menge aller ganzen Zahlen im Intervall [-32768, 32767]. Der Datentyp bestimmt, wie die Informationen als Dualzahlen im Speicher zu verschlüsseln sind, und wie viel Speicherplatz für die Variabel benötigt wird. So werden Variablen vom Typ Integer vom Dezimal- ins Dualsystem konvertiert, und für sie ein Speicherplatz von 32bit = 4Byte reserviert.

Die primitiven Typen sind im Namespace System definiert:

VB.NET

CTS

Wertebereich

Literale

Boolean

System.Boolean

{true, false}

true


System.SByte

[-128, 127]

99

Byte

System.Byte

[0, 255]

255

Char

System.Char

[0, 65535]

"A"C

Short

System.Int16

[-32768, 32767]

199S


System.UInt16

[0, 65535]

199

Integer

System.Int32

[-2147483648, 2147483647]

199


System.UInt32

[0, 4294967295]

199 oder 199U

Long

System.Int64

[-9223372036854775808, 9223372036854775807]

199 oder 199L


System.UInt32

[0, 18446744073709551615]

199 oder 199UL

Single

System.Single

[-3.402823E+38, 3.402823E+38]

3.14 oder 3.14F

Double

System.Double

[-1.79769313486232E+308, 1.79769313486232E+308]

9.0 oder 9

Decimal

System.Decimal

[0, 79.228.162.514.264.337.593.543.950.335]

125.99D

String

System.String

0- 2 Mrd. Unicodezeichen

"Hallo Welt"

Date

System.DateTime

00001-1-1 0:0:0 bis 9999-12-31 23:59:59

#5/31/1993#

Object

System.Object

 

 

1.3.6.4 Enumerations als Teilmengen von Integer

Endliche Mengen lassen sich durch Aufzählungen modellieren (Enumarations). Beipiel:

Enum enumLaengenEinheiten
  mm
  cm
  dm
  m
  km
  AE
  LichtJahr
End Enum

Enumerationen sind Teilmenge vom Typ int. Die Symbole in einem enum- Block vom Compiler automatisch durchnummeriert, beginnend bei 0. Die Nummerierung kann auch vom Programmierer erfolgen, wie der folgende Auszug aus der Beispielbibliothek BatchProcessing zeigt:

'----------------------------------------------------------------------------
' Zustände eines Jobs

Enum JobStates
  undefined   = &H1   ' Job wurde erstellt, aber es ist noch keine JobId 
                      ' definiert worden
  defined     = &H2   ' Job wurde neu erstellt und JopId ist definiert
  waiting     = &H4   ' Job befindet sich in der Warteschlange vor der
                      ' Bearbeitungsstation
  processing  = &H8   ' Job wird bearbeitet
  pause     = &H9,    ' Bearbeitung des Jobs pausiert/wurde kurz unterbrochen
  finished    = &H10, ' Job wurde fertiggestellt    
  aborted     = &H20, ' Bearbeitung des Jobs wurde abgebrochen    
End Enum

Der Zugriff auf einen Enum- Wert erfolgt über den Namen des Enums:

mJobState = JobStates.pause

1.3.6.5 Typkonvertierung

1.3.6.5.1 Implizit

Implizite Typkonvertierungen sind zwischen verwandten Datentypen möglich, wenn der Zieltyp mehr Informationen aufnehmen kann als der Quelltyp:




1.3.6.5.2 Explizit

Explizite Typkonvertierungen erzwingen die Umwandlung von einem Typ in einen anderen. Dabei gibt es jedoch verschieden starke Konvertierungsoperatoren. Auf der einen Seite gibt es die VB- Konvertierungsoperatoren, auf der anderen Seite die Operatoren der Klasse Convert.

VB 6

VB.NET

Convert-Klasse

x = CBool(<expr>)

x = CType(<expr>, Boolean)

toBoolean(<expr>)

x = CByte(<expr>)

x = CType(<expr>, Byte)

toByte(<expr>)

x = CChar(<expr>)

x = CType(<expr>, Char)

toChar(<expr>)

x = CDec(<expr>)

x = CType(<expr>, Decimal)

toDecimal(<expr>)

Die Convert Funktionen sind am stärksten Konvertierungsfunktionen. Sie ermöglichen eine Konvertierung zwischen an sich inkompatiblen Typen wie Zahlenwerte in Strings in nummerische Werte von Int32 etc.

1.3.7 Referenztypen

Alle bis hier behandelten Datentypen sind elementar. Das bedeutet z.B. das sie eine feste Länge besitzen, und ihre Werte ihnen direkt zugewiesen werden können.

VB.NET kann aber mit viel komplexeren Daten umgehen, sog. Objekten. Diese können einen variierenden Speicherplatzbedarf zur Laufzeit haben. Ein Zuweisung eines Wertes direkt an sie ist nicht möglich, da sich sich in der Regel aus vielen elementaren Teildaten, Eigenschaften genannt, zusammensetzen. Diese Art von Typen sind nicht elementar, und werden Referenztypen genannt.

1.3.7.1 Objekt- Variablen

Objekte werden im Arbeitsspeicher anders verwaltet als Variablen primitiven Typs. Sie werden im sog. Freispeicher aufbewahrt, während die Variablen im Stapelspeicher residieren.

VB.NET kann nur direkt auf Variablen zugreifen. Mit folgendem Trick gelingt der Zugriff auf Objekte: es wird eine Variable angelegt und in diese die Speicheradresse des Objekts geschrieben. Beim Zugriff auf die Variable leitet die Laufzeitumgebung von VB.NET dann weiter auf das Objekt.




Das Ablegen der Speicheradresse eines Objekts in einer Variable erfolgt mittels eines speziellen Zuweisungsoperators, der Set- Anweisung:

Dim myUri as Uri
uri = New Uri

Der Zugriff auf das Objekt erfolgt über den Variablenname. Auf Eigenschaften und Methoden des Objektes kann mittels des . Operators zugegriffen werden:

myUri.Host

1.3.7.2 System.Object - gemeinsamer Kern aller Referenztypen

Alle Typen sind auf den Basistyp System.Object rückführbar. D.h. alle Typen verfügen über die Eigenschaften und Methoden von System.Object.




1.3.7.3 Wertetypen als Referenztypen: Boxing und Unboxing- Konvertierung

Um auch Wertetypen im Bedarfsfall wie Objekte behandeln zu können, stellt VB.NET einen komfortablen Mechanismus bereit, genannt Boxing-Unboxing.

Wird ein Wertetyp in einem Objektkontext aufgerufen, dann wird temporär auf dem Heap ein Objekt angelegt, und der Wert des Wertetypen hineinkopiert. Dies wird als boxing  bezeichnet.

' zeichen ist ein Wertetyp und 
Dim zeichen as Char = "A"C

' Im folgenden wird der Wertetyp in einem Objektkontext eingesetzt (zeichen.isDigit())
' Hier wird auf dem Heap ein Objekt erzeugt, dessen Kopie zeichen aufnimmt.
Dim aussage as String
aussage = IIF(zeichen.isDigit(), "Ziffer", "keine Ziffer")  

' Wird ein Wertetyp als objekt- Referenz einem unterprogramm übergeben, dann ist er
' in eine Objektbox verpackt. Aus dieser muss er mittels eines Konvertierungsoperators 
' wieder ausgepackt werden
Sub TueWas(oZeichen as Object)

  ' Unboxing- Konvertierung
  Dim zeichen as Char = Ctype(oZeichen, Char)
  ...

End Sub

1.3.8 Typinformationen abrufen

1.3.8.1 is Operator

Der Is- Operator ergibt True, wenn zwei Referenzen auf dasselbe Objekt verweisen:

If ref_a is ref_b
   return True
End IF

1.3.8.2 Typvergleich mittels TypeOf ... is

Mittels des typeof- Operators kann zu einer Klasse ein Typ- Objekt erzeugt werden. Er entspricht der statischen Methode System.Type.GetType(..).

if TypeOf a Is Integer Then 
   ....
end if

1.3.8.3 GetType() Methode

Jedes Objekt erbt von der .net Wurzel System.Object die Methode GetType. Diese liefert ein Type- Objekt, welches alle Metainformationen zu einem Typ enthält.

1.3.8.4 GetType(x as Object) - Operator

Der GetType(x as Object)- Operator ermöglicht es, zu einem beliebigen Typ (Klassenname) das Type- Objekt zu bestimmen, ohne das ein Instanz notwendig ist.

If strT1.GetType() = GetType(String) Then
   Console.WriteLine("strT1 ist ein String")
End If

1.3.9 Blockstruktur, Sichtbarkeit und Lebensdauer von Variablen




1.3.9.1 Statische Variablen

In Sonderfällen kann es sinnvoll sein, die Lebensdauer von Variablen in Funktionen und Prozeduren über die Blockgrenzen hinaus zu verlängern. Soll z.B. Protokolliert werden, wie oft eine Methode in einem Programm schon aufgerufen wurde, dann muß eine Zähler variable bereitgestellt werden, die wie eine Globale Variable zu Programmstart initialisiert und erst bei Programmende vernichtet wird.

Module Utils

   Function MakeID() as Long

     static id as Long = 0
     id += 1
     return id

   End Function

End Module

1.3.10 Unterprogramm- und Funktionsblöcke

Die grundlegende Blockstruktur erfordert den Einschluss von Anweisungen in Blöcken.

Ein Unterprogrammblock ist eine benannte Anweisungsliste. Über diesen Namen kann die Anweisungsliste gestartet werden.

Eingerahmt wird der Unterprogrammblock wird mit den Schlüsselwörtern Sub … End Sub.

Sub PrintCurrentTime()
    Dim dateTime As Date
    dateTime = Now
    Debug.Print ("Uhzeit " & dateTime.ToShortTimeString)
End Sub

Aufgerufen wird es über den Namen

PrintCurrentTime()

1.3.10.1 Implementierung des EVA- Prinzips mit Unterprogrammen

Ein Grundprinzip der Programmierung ist die Eingabe - Verarbeitung - Ausgabe, kurz EVA.


Ein Beispiel dafür ist die Umrechnung von Polarkoordinaten (Drehwinkel + Abstand) in kartesische Koordinaten (x,y). Das rotierende Flughafenradar erfasst z.B. die Position eines Flugzeugs in Polarkoordinaten. Die umgerechnete y Koordinate entspricht der Flughöhe.




Die Umrechnung kann als VB.NET Unterprogramm implementiert werden. Eingaben und Ausgaben werden dabei über eine Parameterliste abgewickelt. In dieser werden alle Ein- und Ausgaben deklariert analog den Variablendeklarationen.

Sub PolarToKartesian(r as Double, Phi as Double, ByRef x as Double, ByRef y as Double)
             |      |__________________________| |___________________________________|
             |                    |                                 |
      Unterprogramm-       Eingabeparameter                   Ausgabeparameter
      name

Eingabeparameter sind vergleichbar mit lokalen Variablen des Unterprogramms, die beim Programmaufruf mit Daten aus dem Hauptprogramm initialisiert werden:

Dim x as Double : y as Double

PolarToKartesian(1591.2, 45.3,   x,  y)  ' Programmaufruf
                   |_______|     |___|
                       |           |
 Eingaben werden mit Daten aus   Als Ausgaben werden Referenzen auf Variablen 
 Hauptprogramm initialisiert     aus dem Hauptprogramm übergeben



Sind die Ausgabeparametern von primitiven Typ (wie. z.B. Double, Integer, …) dann müssen sie mit dem Schlüsselwort ByRef gekennzeichnet werden, damit sie als Referenzen (=Hauptspeicheradressen, Zeiger) übergeben werden. So entspricht jede Zuweisung an einen Ausgabeparameter im Unterprogramm einer Zuweisung der zugehörigen Variable im Hauptprogramm.

Sind die Ausgabeparameter Objekte (= Referenztypen), dann ist kein ByRef erforderlich, da Objekte immer als Referenzen übergeben werden.

Anbei die vollständige implementierung der Koordinatenumrechnung:

' Rechnet polare in kartesische Koordinaten um

Sub PolarToCartesian(r As Double, phi As Double, ByRef x As Double, ByRef y As Double)
    
    Dim phiInRad As Double
    phiInRad = (Application.Pi * phi) / 180#
    
    x = r * Math.Cos(phiInRad)
    y = r * Math.Sin(phiInRad)
    
End Sub
1.3.10.1.1 Allgemeine Definition eines Sub- Blocks
  Sub <Funktionsname>(<Parameterliste>)
   Anweisung 1
   Anweisung 2
       :
   Anweisung n
   return <Ausdruck>
End Sub

1.3.10.2 Selbstdefinierte Funktionen

Die Menge der vordefinierten VB.NET- Funktionen kann mittels Funktionsblöcke erweitert werden. Funktionsblöcke rahmen die Schlüsselwörter Function … End Function ein.

Eingaben werden wie bei Unterprogrammen als Parameterliste übergeben.

' Funktion, die eine römische Zahl auf einen Zahlenwert
' in arabischer Notation abbildet
Function RomToArabFunc(romNum As String) As Long
                       |_______________|  |____|
                               |            |
                            Argument     Typ des Funktionswertes

Im Funktionsblock wird die Abbildungsvorschrift durch eine Anweisungsliste implementiert. Der Funktionswert wird durch Zuweisen an den Funktionsnamen festgelegt.

' Funktion, die eine römische Zahl auf einen Zahlenwert
' in arabischer Notation abbildet
Function RomToArabFunc(romNum As String) As Long
    ' Hilfsvariable anlegen, die Ergebnis der Konvertierung aufnimmt
    Dim arabValue As Long
    
    ' Unterprogramm zur Konvertierung von römisch in 
    ' arabische Zahlendarstellung aufrufen
    RomToArab(romNum, arabValue)
    
    ' Funktionswert druch Zuweisen an Funktionsnamen festlegen
    return arabValue
    
End Function
1.3.10.2.1 Allgemeine Definition eines Function- Block
  Function <Funktionsname>(<Parameterliste>)
   Anweisung 1
   Anweisung 2
       :
   Anweisung n
   return <Ausdruck>
End Function

1.3.11 Steuerung der Programmausführung

In seiner Grundbetriebsart lädt der Computer eine Anweisung nach der anderen aus dem Arbeitsspeicher und führt sie aus. Viele Algorithmen erfordern jedoch, dass einige Abschnitte aus den Anweisungslisten nur unter bestimmten Bedingungen auszuführen sind (z.B. „Wenn bis zum Tag X kein Zahlungseingang erfolgte, dann Mahnung abschicken“) oder wiederholt werden müssen. Um dies zu ermöglichen, verfügt der Computer über spezielle Anweisungen, die den Befehlszählerstand manipulieren. Sie bewirken, dass nach ihrer Ausführung das Programm an einer beliebigen neuen Adresse fortgesetzt wird. Aufgrund dieser Wirkung werden sie Sprunganweisungen genannt.

In VB.NET sind die Sprunganweisungen in Form von bedingt ausführbaren, oder bedingt wiederholbaren Blöcke.

1.3.11.1.1 Bedingt ausführbarer Anweisungsblock

Der bedingt ausführbare Anweisungsblock entspricht einer Wenn ... dann Regel.

IF Bedingung Then
   Anweisung 1
   Anweisung 2
       :
   Anweisung n
End IF

Trifft die Bedingung nicht zu, dann kann anschließend mit dem Schlüsselwort Else ein Anweisungsblock als Alternative formuliert werden:

IF Bedingung Then
   Anweisung 1
       :
   Anweisung n
Else
   Anweisung n+1
       :
   Anweisung n+m
End IF

Gibt es mehrere Alternativen, die von Zusatzbedingungen abhängig sind, dann können diese in ElseIf- Blöcken formuliert werden:

IF Bedingung Then
   Anweisung 1
       :
   Anweisung n
ElseIF Bedingung2
   Anweisung n+1
       :
   Anweisung n+m
End IF

Ist die Ausführung mehrerer Anweisungsblöcke vom Wert eines einzigen Ausdrucks abhängig, dann kann alternativ zu ElseIf eine vereinfachte Schreibweise mittels Select Case Block gewählt werden:

Select Case Ausdruck
Case Wert1
     Anweisung 1
         :
     Anweisung n
Case Wert2
     Anweisung n+1
         :
     Anweisung n+m
Case Wert3
  :
Case Else
     Anweisung 
End Select 
1.3.11.1.2 Bedingt wiederholbarer Anweisungsblock

Es handelt sich hierbei um Anweisungsblöcke, die sooft wiederholt werden, solange eine Bedingung zutrifft (wahr ist). Sie werden gewöhnlich als Schleifen bezeichnet. Zwei elementare Formen der Schleifen werden unterschieden: die abweisende, und die nicht abweisende Schleife.

Bei der abweisenden Schleife wird stets am Anfang des Anweisungsblockes geprüft, ob die Bedingung für eine Ausführung erfüllt ist. Wenn ja, dann wird der Anweisungsblock ausgeführt. Gilt die Bedingung weiterhin, dann erfolgt eine wiederholte Ausführung des Anweisungsblockes. Trifft die Bedingung nicht zu, dann wird der Anweisungsblock übersprungen.

DO Bedingung
   Anweisung 1
   Anweisung 2
       :
   Anweisung n
Loop

Bei der nichtabweisenden Schleife wird die Bedingung erst am Ende des Anweisungsblockes überprüft. Trifft sie zu, dann wird der Anweisungsblock wiederholt. Sonst wird mit der nachfolgenden Anweisung fortgesetzt.

Da die Bedingung erst am Ende des Abschnittes überprüft wird, wird der Anweisungsblock immer mindestens einmal ausgeführt.

DO
   Anweisung 1
   Anweisung 2
       :
   Anweisung n
Loop Bedingung

Sind Listen fester Länge zu durchlaufen, oder Berechnung mit einer vorgegebenen Zahl wiederholt auszuführen, dann bietet sich die For...to ... next Schleife an:

FOR i = <Startwert> TO <Endwert> [STEP <Schrittweite>]
   Anweisung 1
   Anweisung 2
       :
   Anweisung n
Next

Sollen alle Elemente einer Liste verarbeitet werden, dann kann der For Each Anweisungsblock eingesetzt werden:

For Each el [as Type] in {Liste} 
   ...
Next

1.3.12 Arrays

Zur Verarbeitung tabellarischer Daten dienen in VB Arrays. Durch die Deklaration eines Arrays werden im RAM n Speicherplätze zur Aufnahme von Werten des Typs x reserviert.


Arrays sind Referenztypen.

Deklaration eines Arrays hat folgenden Aufbau:

Dim a(3) as Integer
    | |       +----- Typ der Einträge
    | +------------- größter Index eines Eintrages
    +--------------- Name des Arrays

Nach einer Arraydeklaration ergibt sich folgendes Format im Arbeitspeicher:

Der Zugriff auf die Elemente eines Arrays erfolgt über den Namen und den Index:

Dim x as Integer

' x hat nach Ausführung dieser Anweisung den Wert 2
x = a(2)
    | +--- Indize (Platznummer)
    +----- Arrayname

Die Indizes können dynamisch zur Laufzeit festgelegt werden. Damit ist eine komfortable Verarbeitung der Arraydaten in Schleifen möglich:

Dim i as Integer, s as Long

' s enthält nach der Schleife die Summe aller Einträge
for i = 0 to 3
   s = s + a(i)
next

1.3.12.1 Initialisierung von Arrays

Dim primz() as Integer = {2, 3, 5, 7, 11}

1.3.12.2 Anzahl der Einträge bestimmen

Dim anz_elems as Int32 = primz.length  'Anzahl der Elemente in über alle Dimensionen
Dim anz_dims  as Int32 = primz.rank    'Anzahl der Dimensionen eines Arrays

1.3.12.3 Zuweisen und Kopieren

Durch Zuweisung wird nur eine Referenz erzeugt, jedoch keine Kopie.

Dim prim2() As Int32 = prim

Die Methode Clone() eines Arrays erzeugt eine 1:1 Kopie

Dim prim3() As Int32 = prim.Clone()

1.3.12.4 Redimensionierung

Zur Laufzeit können die Dimensionen eines dynamischen Arrays mit der Prozedur ReDim <arrayName>( <neuerMaxIndex>) vergrößert und verkleinert werden wie folgt:

:
ReDim Preserve a(6) 
' Das Array wird um 3 Elemente vergrößert, wobei der alte Inhalt erhalten bleibt
:
ReDim a(9) 
' Das Array wird wiederum um 3 Elemente vergrößert. Diesmal wird der alte Inhalt jedoch nicht
' erhalten (Preserve fehlt)

1.3.12.5 Besuchen aller Einträge eines Arrays mittels foreach - Schleife

For Each quadrat as Integer in quadrate) 
   Console.WriteLine(quadrat);
Next

1.3.12.6 Arrays sortieren

Dim planeten() as String = {"Merkur", "Venus", "Erde", "Mars", "Jupiter", "Saturn"}
Array.Sort(planeten);

1.3.13 Chars

1.3.13.1 Objektmodell von char

1.3.13.2 Chars und Unicode

.NET unterstützt Unicode. Quelltexte können als Unicode- Dateien verpackt werden, und Zeichentypen wie char und string basieren auf 16bit Unicode. Folgendes Windows- Programm liefert einen Dialog mit einem kyrillischen Text:

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Vom Windows Form Designer generierter Code "
  :
#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ' char verarbeiten Unicode- Zeichen
        Dim c1, c2, c3, c4, c5, c6, c7 As Char

        c1 = ChrW(1051)
        c2 = ChrW(1070)
        c3 = ChrW(1041)
        c4 = ChrW(1054)
        c5 = ChrW(1042)
        c6 = ChrW(1068)

        Dim str As String

        str += c1.ToString + c2.ToString + c3.ToString + c4.ToString + c5.ToString + c6.ToString

        lblDasWichtigste.Text = str


    End Sub
End Class

Mittels der Funktion ChrW(Zahl) wird eine nummerischer Unicode in ein Unicodezeichen umgewandelt. Der String aus den Unicodezeichen wird schließlich über ein Label (lblDasWichtigste) im Formular angezeigt.

1.3.13.2.1 Aufg.:
  1. Programm, welches in einer Textbox eine Teilmenge des Unicodes als Zeichencodetabelle ausgibt. Bsp: Währungssymbole von 20A1 bis 20AF

  2. Programm, welches alle Satzzeichen im Unicode ausfiltert und anzeigt

1.3.14 Zeichenkettenverarbeitung (Strings)

1.3.14.1 Zeichenkettenoperatoren

Zeichenketten können mit den Operatoren + und & zu einer neuen verbunden werden.

Dim txt as String
' verbinden mittels +
txt = "Betrag " + "99" + " Euro"

'verbinden mittels &. Zahlenwerte werden automatisch in Strings gewandelt
txt = "Betrag " & 99 & " Euro"

1.3.14.2 Teilstrings ausschneiden

Dim txt as String = "Anton,Berta,Cäsar"
Console.WriteLine(txt.Substring(6, 5))

1.3.14.3 Umwandeln in Groß- oder Kleinschreibung

Dim txt as String = "Hallo Welt"

Console.WriteLine("Alles groß: {0}", txt.ToUpper())
Console.WriteLine("Alles klein: {0}", txt.ToLower())

1.3.14.4 Startposition von Teilstrings bestimmen

Dim txt as String = "Anton,Berta,Cäsar"
Dim pos as Int32  = txt.IndexOf("Berta")

1.3.14.5 Einfügen in Strings

Dim txt as String = "Anton,Berta,Cäsar"
txt = txt.Insert(txt.IndexOf("Berta"), "Aladin,")

1.3.14.6 Auftrennen von Zeichenfolgen

Dim txt as String = "Anton,Berta,Cäsar";
Dim namen() as String = txt.Split(","c);

1.3.14.7 Splitten mittels Reguläre Ausdrücke

Dim r As Regex = New Regex("\s*=\s*")
Dim aw() As String = r.Split("Durchmesser = 199")
Console.WriteLine("Attribut {0} hat Wert {1}", aw(0), aw(1))

1.3.14.8 Ersetzen mittels Regulärer Ausdrücke

' In folgendem String sollen alle Vorkommen von keinen durch [] markiert werden
txt = "Das Pferd frisst keinen Gurkensalat. Ich habe keine Ahnung warum"
Dim txt_neu As String = Regex.Replace(txt, "keinen", "[keinen]")
Console.WriteLine(txt_neu)

1.3.14.9 Aufgaben

  1. Romzahlkonverter

  2. Zählen der Buchstabenhäufigkeit in gegebenen Texten

1.3.15 Datum und Uhrzeit

Grundlage für die Datumsberechnung ist ein linearer Zeitstrahl, der in Ticks unterteilt ist. Ein Tick hat die Dauer von 10-7s (zehnmillionstel Sekunde). Zeitpunkte auf dem Zeitstrahl werden durch Werte vom Typ Date angegeben. Ein Datumswert wird hat das Format #M/D/YYYY# (amerikanisches Datumsformat).


Date basiert auf System.DateTime. Die Konstruktoren von System.DateTime sind mehrfach überladen:

dim dat1 as New System.DateTime(Ticks)
dim dat2 as New System.DateTime(YYYY, MM, DD)
dim dat2 as New System.DateTime(YYYY, MM, DD, hh, min, sec, millisec)

Zur Darstellung von Zeiträumen dient die Klasse TimeSpan.

1.3.16 Datenstrukturen

Datenstrukturen werden deklariert durch Blöcke mit dem Schlüsselwort Structure (bei VB 6.0 Type). Jedes Element einer Strukture muß mit einem Zugriffsmodifikator versehen werden (Public, friend, Protected, Private). Dim entspricht dem Zugriffsmodifikator Public.

Structure Point 
  
  Dim x as Double
  Dim y as Double

End Structure

1.3.16.1 Merkmale von Strukturen

1.3.17 Einsprungadressen speichern (Delegates)

Jedes Unterprogramm, jede Funktion hat im Arbeitsspeicher einen Startadresse, auch Einsprungadresse genannt. Ein Unterprogramm startet, indem der Befehlszähler der CPU mit dieser Einsprungadresse geladen wird.




Wie alle Daten können auch Einsprungadressen gespeichert werden. Variablen, die Einsprungadressen speichern, werden Delegates genannt.


Der Datentyp der Delegate- Variablen, die Einsprungadressen speichern, schränkt auf Einsprungadressen von Unterprogramme mit einer fest definierten Parameterliste und Rückgabewert ein. Die Variablen bieten dazu ein Invoke- Methode an, über welche die Einsprungadresse mit der passenden Parameterliste aufgerufen werden kann.

Delegate- Datentypen können selbst definiert werden mit dem Schlüsselwort Delegate:

' Klasse erzeugen, in der eine Einsprungadresse einer binären Funktion abgelegt werden kann
Delegate Function DGBinär(a As Double, b As Double) As Double

Eine Delegate- Variable wird wie ein Objekt angelegt, wobei die Einsprungadresse dem Konstruktor als Parameter zu übergeben ist. Mittels AddressOf Schlüsselwort wird dabei die Einsprungadresse bestimmt zu einem gegeben Unterprogrammnamen:

Function Add(a As Double, b As Double) As Double
    Return a + b
End Function

Function Subtract(a As Double, b As Double) As Double
    Return a - b
End Function

Sub Main()

    Dim op As DGBinär

    ' Adresse von Add mittels AddressOf- Operator ermitteln und im Delegate speichern
     op = New DGBinär(AddressOf Add)

    ' Aufrufen eines Unterprogrammes über die Einsprungadresse
    Dim Summe = op.Add(3, 9)

    ' Weitere Einsprungadressen können mit AddHandler hinzugefügt werden
    AddHandler op, AddressOf Subtract  

     ...

End Sub

1.3.18 Primzahlscanner imperativ implementieren

Teilaufgabe 1 wird durch folgende Befehlssequenz gelöst (Pseudokode):

  Primzahltest(In: Prüfling, Out: IstPrimzahl)
  Dim Teiler = 2
  Wiederhole bis Teiler = Prüfling / 2 + 1
    Wenn Prüfling Mod Teiler = 0 dann 
      IstPrimzahl := false
      Goto Ende Primzahltest
    Sonst
      Teiler := Teiler + 1
    Ende Wenn, dann, sonst
  Ende Wiederholung
  IstPrimzahl = t
Ende Primzahltest

Teilaufgabe 2 implementiert folgende Befehlssequenz



  Primzahlsuche(In: a, In: b, Out: ListePrimzahlen)

  Dim Prüfling = a 
  Wiederhole bis Prüfling = b + 1
    Dim IstPrimzahl
    Primzahltest(In: Prüfling, Out: IstPrimzahl)
    Wenn IstPrimzahl dann
       ListePrimzahlen erweitern um Prüfling
    Ende Wenn, dann
    Prüfling = Prüfling + 1
  Ende Wiederholung 

Ende Primzahlsuche

1.4 Objektorientierte Programmierung in VB.NET

Die objektorientierte Programmierung ist eine Erweiterung der klassischen, prozeduralen Programmierung um Entwurfsmethoden und Ausdrucksmittel, durch welche

  1. die Abbildung von Geschäftsprozessen auf Programmstrukturen vereinfacht,

  2. und der Programmcode transparenter wird.

Von zentraler Bedeutung sind dabei Objekte:

Definition

Objekt

Ein Objekt steht für ein konkretes Ding (z.B. Der rote Ferrari von Fred Vollgas oder die Tabelle 1 im Arbeitsblatt Umsätze).

Objekte besitzen einen inneren Zustand. Z.B. hat der rote Ferrari von Fred Vollgas eine Geschwindigkeit, und die Zelle $A1 in Tabelle 1 des Wert 99. Der Zugriff auf den inneren Zustand erfolgt über Eigenschaften.

Objekte kann man beeinflussen, indem man ihnen Nachrichten sendet. Z.B. kann man den roten Ferrari in einer Simulation auffordern, die Geschwindigkeit auf 100 km/h zu reduzieren, oder dem Tabellenblatt mitteilen, den Hintergrund der Zelle $A1 rot zu färben.

Zustandsänderungen in Objekten können Ereignisse auslösen. Schert vor dem Ferrari urplötzlich aus der rechten Spur eine grüne Ente aus, die den linken Fahrstreifen mit Tempo 100 km/h blockiert, dann wird der Ferrari- Fahrer wütend, und hupt (= löst das Ereignis Hupen) aus. Durch dieses Ereignis können wiederum eine Reihe von Nachrichten erzeugt werden, die an die umgebenden Objekte gesendet werden (z.B. Nachricht Hupsignal an die grüne Ente gerichtet).



1.4.1 Graphische Darstellung von Objekten mittels Objektdiagramm

Die objektorientierte Sichtweise erleichtert die die notwendige Formalisierung von Aufgaben- Problemstellungen bei der Softwareentwicklung, indem sie eine 1:1 Abbildung der Realität auf den Entwurf ermöglicht. 1:1 bedeutet nicht, das alle Details der Realität im Entwurf nachgebildet werden. Der Entwurf abstrahiert nach wie vor von der Realität, indem er die Abbildung auf die für die Aufgabenstellung wesentlichen Eigenschaften und Prozesse einschränkt. Jedoch ist weniger Abstraktion notwendig als bei der rein Prozeduralen Programmierung, da diese zusätzlich erfordert, alle Objekte in Mengen aus Daten und diese manipulierende Prozeduren aufzulösen.

Objektdiagramm

Objekt




alternativ:

Objekt
  |
  +- Eigenschaft
  |
  +- M: Methode(Param1, Param2)
  |
  +- C: Collection
  |
  +- E: Event





Die Erweiterung zu objektorientierten Sprache erfolgt in VB.NET im wesentlichen durch den Modul- und Class- Block.

Module

Class

Alle Elemente sind implizit Shared

Zugriff auf die Elemente muss individuell festgelegt werden

Im Unterschied zu den Anweisungsblöcken der strukturierten Programmierung enthalten Modul und Class- Block keine Anweisungen sondern stellen vielmehr Container dar, die Functions, Sub's und Variablen aufnehmen, die zu einem Aufgabenkomplex gehören/in einem sachlichen Zusammenhang stehen.

1.4.2 Beispiel: Klasse Stoppuhr

Als Beispiel für eine Klassendeklaration und Objekte diene die Klasse Stoppuhr. Mit einem Stoppuhr- Objekt kann in einem Programm die Rechenzeit für aufwendige Berechnungen gemessen werden.


Beispielanwendung:

<TestMethod()> Public Sub StoppuhrTest()

   ' Stoppuhrobjekt anlegen
   Dim uhr1 As New VB.OO.Klassenlib.Stoppuhr

   With uhr1

      ' Zeitmessung starten
      .Start()

      ' Simulation einer aufwendigen Berechnung
      System.Threading.Thread.Sleep(ZeitLimit)

      ' Zeitmessung stoppen
      .Stopp()

      ' Zeitmessung auswerten
       Assert.IsTrue(.ZeitInMs > ZeitLimit - 1)

   End With
End Sub

1.4.3 Innerer Zustand

Objekte können wie Variablen Informationen speichern. Dieser objekt- interne Informationsspeicher wird als Innerer Zustand bezeichnet.

Implementiert wird der innere Zustand durch Variablen, auch Felder genannt, die zu Beginn eines Klassenmoduls mittels Dim- Anweisungen zu vereinbaren sind:

' Innere Zustand der Stoppuhrklasse:
' Zeitpunkte beim Start und Stop auf dem System.DateTime- Zeitstrahl
Dim _TicksBeimStart As Long
Dim _TicksBeimStopp As Long

' Name der Stoppuhr
Dim _Name As String

1.4.4 Methoden

Methoden sind für den externen Zugriff freigegebene Prozeduren (Sub's) oder Funktionen (Function's). Aus der Perspektive der Objektorientierung sind Methoden Nachrichten, die an ein Objekt gesendet werden. Diese Nachrichten beeinflussen den inneren Zustand eines Objektes.




In einer etwas praktischeren Sichtweise können wir Methoden als Operationen betrachten, die abhängig vom inneren Zustand des Objektes sind.

In einem Klassenmodul werden Methoden als öffentliche Sub's oder Function's definiert:

' Methode = Nachricht an Stoppuhr, Zeitmessung zu starten
Sub Start()
    Reset()
End Sub

' Methode = Nachricht an Stoppuhr, Zeitmessung zu stoppen 
Sub Stopp()
    _TicksBeimStopp = DateTime.Now.Ticks

    ' Wenn das definierte Zeitlimit überschritten wurde, feuert ein Ereignis
    If ZeitlimitInMs > -1.0 And ZeitInMs > ZeitlimitInMs Then
        RaiseEvent ZeitlimitUeberschritten(Name)
    End If
End Sub

' Methode = Nachricht an StoppUhr, inneren Zustand zurückzusetzen
Sub Reset()
    _TicksBeimStart = DateTime.Now.Ticks
    _TicksBeimStopp = _TicksBeimStart
End Sub

1.4.5 Eigenschaften

Eigenschaften sind die Schnittstellen zum inneren Zustand eines Objektes. Sie werden in VB.NET durch benannte Paare von Methoden implementiert, die in einem Property- Block eingeschlossen sind. Sie ermöglichen die Manipulation des inneren Zustandes.

Property <Name der Eigenschaft> As <Datentyp>
        ' Der Getter
        Get
            Return <Ausdruck, der auf inneren Zustand zugreift>
        End Get

         ' Der Setter
        Set(value As <Datentyp>)
            ' Zugriff auf inneren Zustand
        End Set
End Property

Im allgemeinen ist der direkte Zugriff auf die Felder verboten, welche den inneren Zustand eines Objektes implementieren. In VB.NET kann man jedoch Felder Public deklarieren, wodurch der direkten Zugriff auf den inneren Zustand möglich wird. Das verletzt jedoch die Prinzipien der objektorientierten Programmierung und kann als Relikt aus der "Computersteinzeit" betrachtet werden !




Eine Methode des Paars wird Setter genannt. Sie wird mit dem Schlüsselwort Set markiert und ermöglicht einen Schreibzugriff auf den inneren Zustand:

  
    
      ' Der Setter
    
  
Set(value As <Datentyp>)
  ' Zugriff auf inneren Zustand
End Set

Aufgerufen wird der Setter, wenn man der Eigenschaft einen neuen Wert zuweist:

  Objekt_X.MyProp = 
  Neuer Wert

Die andere Methode ist der Getter. Er ermöglicht einen Lesezugriff auf den inneren Zustand:

' Der Getter
Get
   Return <Ausdruck, der auf inneren Zustand zugreift>
End Get

Aufgerufen wird der Getter, wenn man den Wert aus der Eigenschaft ausliest:

Dim meineVariable as Integer
meineVariable = Objekt_X.MyProp

1.4.5.1 Beispiele für Eigenschften in der Stoppuhrklasse:

' Primitive Eigenschaft, die einen inneren Zustand liest und schreibt, der vom 
' VB- Compiler implizit angelegt wird: Grenzwert für ZeitInMs, unterhalb dem bei Stopp kein 
' ZeitlimitUeberschritten- Event gefeuert wird.    

Property ZeitlimitInMs As Double

' Name der Stoppuhr (nur lesbar)

ReadOnly Property Name As String
    Get
        Return _Name
    End Get
End Property



' Lese- und beschreibbare Eigenschaft: ermölicht das Lesen und Schreiben
' des Zeitlimits in Sekunden

Property ZeitlimitInSec As Double
    Get
        Return ZeitlimitInMs / 1000.0
    End Get
    Set(value As Double)
        ZeitlimitInMs = value * 1000.0
    End Set
End Property



1.4.6 Ereignisse

Wenn ein Objekt einen besonderen Zustand annimmt, kann es abhängig von der Aufgabenstellung erforderlich sein, andere Objekte darüber zu informieren. Diese Situation wird Ereignis genannt.




Die Stoppuhr kann über die Eigenschaft ZeitlimitInMs so konfiguriert werden, dass sie ein ein Ereignis "feuert", wenn eine Einstellbare Zeitspanne in der Messung überschritten wurde. Das Feuern erfolgt mit RaiseEvent in der Stopp- Methode:

Sub Stopp()
    _TicksBeimStopp = DateTime.Now.Ticks

    ' Wenn das definierte Zeitlimit überschritten wurde, feuert ein Ereignis
    If ZeitlimitInMs > -1.0 And ZeitInMs > ZeitlimitInMs Then
        RaiseEvent ZeitlimitUeberschritten(Name)
    End If
End Sub

Ein "Ereignis feuern" kann man sich wie einen Methodenaufruf vorstellen. Jedoch ist die Methode nicht fest beim Implementieren der Stoppuhr definiert worden. Stattdessen kann die Methode nachträglich an das Ereignis "gebunden" werden mit dem AddHandler Befehl. Man nennt die an das Ereignis gebundenen Methoden auch Eventhandler.

' Eventhandler für Zeitmessung
Sub ZeitlimitUebrschrittenEventHandler(Uhrname As String)
   hatZeitlimitUeberschritten = True
End Sub

Const ZeitLimit As Integer = 500
Dim hatZeitlimitUeberschritten As Boolean = False

<TestMethod()> Public Sub StoppuhrTest()

   ' Stoppuhrobjekt anlegen
   Dim uhr1 As New VB.OO.Klassenlib.Stoppuhr

   With uhr1      

       Dim hatZeitlimitUeberschritten2 As Boolean = False

       ' Eventhandler registrieren
        AddHandler .ZeitlimitUeberschritten, AddressOf ZeitlimitUebrschrittenEventHandler
        AddHandler .ZeitlimitUeberschritten, Sub(Name) hatZeitlimitUeberschritten2 = True

       ' Zeitlimit konfigurieren
       .ZeitlimitInMs = ZeitLimit - 1

       ' Zeitmessung starten
       .Start()

       ' Simulation einer aufwendigen Berechnung
       System.Threading.Thread.Sleep(ZeitLimit)

       ' Zeitmessung beenden
       .Stopp()

       ' Zeitmessung auswerten
        Assert.IsTrue(hatZeitlimitUeberschritten And hatZeitlimitUeberschritten2)

   End With
End Sub

Aus technischer Sicht ist ein Ereignis ein Speicherort, an dem andere Objekte Einsprungadressen von Methoden (Eventhandler) hinterlegen, die starten sollen, wenn das Ereignis eintritt bzw. "feuert".

1.4.7 Primzahlscanner objektorientiert implementieren

Teilaufgabe 1 wird durch Zahlenobjekte gelöst, die eine IstPrimzahl- Methode besitzen. Wir senden bildlich gesprochen dem Zahlenobjekt eine Anfrage, ob es eine Primzahl ist oder nicht, indem wir seine IstPrimzahl- Methode aufrufen. Gibt diese true zurück, dann ist das Zahlenobjekt eine Primzahl, sonst nicht.


Teilaufgabe 2 wird durch zwei Objekte gelöst. Das eine (Zahlenbereich) erzeugt für vorgegebene Grenzen eine Liste aus Zahlenobjekten, die alle Zahlen innerhalb der Grenzen als Zahlenobjekte umfasst.




Das andere (Primzahlscanner) filtert die Liste, indem es jeden Eintrag über die IstPrimzahl- Methode befragt, ob es eine Primzahl ist. Objekte von Primzahlen werden in die Ergebnisliste kopiert




1.5 Funktionale Programmierung in VB.NET

Die Mathematiker entwickelten in Jahrhunderten eine eigene Sprache: die Sprache der Formeln und Funktionen. Diese Sprache besteht aus Operatoren wie z.B. + und *, Zahlendarstellungen und Symbolen für Konstanten (z.B. π) und Veränderliche (z.B. x, D). Beispiele sind die Formel zur Berechnung des Kreisumfanges:

U = π * D

1.5.1 Funktionen

Allgemeiner kann man den Zusammenhang zwischen Durchmesser D eines Kreises und seinem Umfang U durch eine Funktion darstellen:

f(D): D → π * D

Den Ausdruck kann man lesen als: die Funktion f(D) ordnet jedem Durchmesser D einen Kreisumfang f(D) = π * D zu. Diese Darstellung wurde in den 1930-er Jahren zu einem präzisen Formalismus ausgearbeitet (Alonzo Church, Stephen Kleene: Lambda- Kalkül), welcher die Grundlage für die Familie der funktionalen Programmiersprachen wie z.B. LISP oder F# bildet.

Während die Mathematiker Funktionen gerne mit einem einzigen Buchstaben bezeichnen, und dabei auch noch auf das griechische Alphabet zugreifen, bevorzugen die Programmierer lieber sprechende Namen wie:

Kreisumfang(D): D → π * D

1.5.1.1 Funktionen in VB.NET

Eine Funktion wird in VB.NET durch einen Funktionsblock dargestellt, der mit dem Schlüsselwort Function beginnt. Diesem folgt eine Parameterliste, die alle Veränderlichen auflistet, von denen der Funktionswert abhängt. Im Funktionsblock wird der Funktionswert berechnet und zurückgegeben. Beispielsweise kann die Kreisumfangsfunktion in VB.NET wie folgt dargestellt werden:

  
    ' VB.NET
  
Function Kreisumfang(D)
    Return Math.Pi * D
End Function

Das return entspricht dabei dem Abbildungspfeil → in der mathematischen Darstellung Kreisumfang(D): D → π * D.

Die Funktion kann nun angewendet werden, indem sie mit einem Argument aufgerufen wird:

' Der Umfang eines Kreises mit 250 cm Durchmesser wird berechnet
Kreisumfang(250)

1.5.1.2 Übungen

Implementieren Sie eine Funktion in VB.NET, die einen gemessene Länge in cm auf eine Länge in m abbildet:

  
    CmInMeter(x): x → x*0,01
  

1.5.1.3 Mathematische Funktionen

Die wichtigsten mathematischen Funktionen sind im Math- Objekt des Namespace System enthalten.

1.5.1.4 Optionale Parameter

Beispiel: Eine Funktion soll implementiert werden, welche den Zeitraum zwischen zwei gegebenen Daten bestimmt. Als Standard wird der Zeitraum in Sekunden ausgegeben. Optional kann die verstrichene Zeit aber auch in Minuten, und Stunden ausgegeben werden:

Function Zeitraum_zwischen(ByVal start As Date, ByVal ende As Date, Optional ByVal einheit As String = "sec")
        Dim ticks As Long = Math.Abs(start.Ticks - ende.Ticks)
        Dim zr As New TimeSpan(ticks)
        Select Case (einheit)
            Case "sec"
                Return zr.TotalSeconds
            Case "min"
                Return zr.TotalMinutes
            Case "hh"
                Return zr.TotalHours
            Case Else
                Throw New Exception("Unbekannte Zeiteinheit")
        End Select

    End Function

Aufgabe:

Die Funktion Zeitraum_zwischen(...) zur Berechnung von Zeiträumen in Minuten und Stunden anwenden.

1.5.1.5 Benannte Parameter

Parameterlisten von Funktionen/Unterprogrammen können lang werden. Zudem müssen beim Aufruf die Reihenfolge der Parameter beachtet werden. Ein Funktionsaufruf erfordert in diesem Fall immer ein Studium der Dokumentationen.

Mittels benannter Parameter kann die Situation entschärft werden. Die Kenntnis der Parameterreihenfolge ist nicht mehr erforderlich, weil die Werte Parameternamen beim Aufruf beigestellt werden. Die richtige Platzierung der Werte in der ursprünglichen Parameterliste erfolgt nun durch den Compiler.

' Funktion mit komplexer Parameterliste
Public Function CreatePlanet(Name As String, _
                             DiameterInKm As Double, _
                             GravityInMeterPerSec As Double) As Planet
        Return New Planet With { _
            .Name = Name, _
            .DiameterInKm = DiameterInKm, _
            .GravityInMeterPerSec = GravityInMeterPerSec _
        }
    End Function

' Aufruf über benannte Parameter
Dim Erde = CreatePlanet(Name:="Erde", _
                        DiameterInKm:=12756, _
                        GravityInMeterPerSec:=9.81)

1.5.1.6 Parameterarrays

Funktionen, die Listen von Werten eines festen Typs abbilden, wird durch Parameterarrays der Einsatz erleichetert:

Beispiel:

Function Sum(ParamArray summanden( ) As Double)

        Dim i As Integer, summe As Double
        For i = 0 To summanden.GetUpperBound(0)
            summe += summanden(i)
        Next

        Return summe

End Function

Gewöhnlich muss zuerst die Liste z.B. durch ein Array implementiert, und dieses dann an die Listenfunktion übergeben werden:

Dim liste() = {3.0, 5.0, 7.0, 11.0, 13.0}
Dim SumListe = Ctx.Sum(liste)

Dank ParamArray- Modifikator kann die Liste auch im Funktionsaufruf erzeugt werden, wodurch eine Zeile Code entfällt, und die Syntax des Sum- Funktionsaufrufes sehr anschaulich wird:

' Dank ParamArray- Zusatz in der Parameterliste wird die Liste 1, 2, ...
' vom Compiler automatisch in ein Array verpackt
Dim summeAus3 = Ctx.Sum(1, 2, 3)
Dim summeAus6 = Ctx.Sum(1, 2, 3, 4, 5, 6)

1.5.1.7 Delegate (Funktionszeiger)

Komponenten wie z.B. Formulare und strombasierte XML- Parser können über Ereignisroutinen (Eventhandler) an die Wünsche des Anwenders angepasst werden. Ereignisroutinen werden individuell vom Anwender erstellt und ähneln gewöhnlichen Prozeduren. Anschießend muss der Komponente die Ereignisroutinen bekannt gemacht werden. In C++ benutzt man dazu z.B. Funktionszeiger. Da VB.NET keine Zeiger bereitstellt, gibt es ein Sprachkonstrukt, die sog. Delegats (Delegierungen). Es handelt sich hierbei letzendlich um typisierte Funktionszeiger, deren Wert jedoch nicht geändert werden darf.

1.5.1.8 Deklaration eines Delegate

Jeder Delegate muss vor seiner Verwendung deklariert werden:

[Zugriffsmodifikator] Delegate Methodendeklaration
z.B.:
' Klasse für Einsprungadressen von Funktionen mit zwei Integerparametern 
' und einem Interger- Rückgabewert
Delegate Function DgBinOp(a As Double, b As Double) As Double
:
Function Add(a As Double, b As Double) As Double
   Return a + b
End Function
:
' Einsprungadresse einer Funktion speichern
Dim op As New DgBinOp(AddressOf Add)

Debug.WriteLine("2 + 5 = " & op(2, 5))

1.5.1.9 Lambda- Ausdrücke

Der Begriff Lambda Ausdruck wurde in einem Formalismus der Mathematiker Alonzo Church und Stephe Kleene in den 1930 Jahren geprägt (Lambda Kalkül ). Dieser Kalkül wird auch als der Urahn der funktionalen Programmierung betrachtet.

Ein Lambda- Ausdruck ist eine Funktionsdeklaration in Form eines Ausdrucks innerhalb eines Funktions- oder Programmblockes.

  Delegate Function DgBinOp(a As Double, b As Double) As Double

' Einsprungadresse eines Lambda- Ausdruckes
' Der Lambdaausdruck ist eine inline definierte anonyme Funktion
op = New DgBinOp(Function(a, b)
                   Return a * b
                 End Function)

 Debug.WriteLine("2 * 5 = " & op(2, 5))

Der Sinn von Lambdaausdrücken erschließt sich, wenn diese z.B. über Parameterlisten an Unterprogramme oder Funktionen übergeben werden, um diese "funktional" zu parametrieren:

  Dim zahlen() As Integer = {3, 55, 78, 32, 94, 99, 103, 108}

' Formulieren kurz und knackig mit Lambdaausdruck
alleDurch2TeilbarenAlsListe = zahlen.Where(Function(z) z Mod 2 = 0).ToArray()



1.5.2 Fallunterscheidungen

Neben den Ausdrücken wie π * D können die Abbilder von Funktionen auch durch Fallunterscheidungen definiert werden. Z.B. kann die Funktion, die jeder Zahl ihren absoluten Betrag zuordnet, wie folgt beschrieben werden:

  Fallunterscheidungen
                     -z      z < 0
|z| = Abs(z): z → 0  für z = 0
                      z      z > 0

z.B. Abs(-5) = 5, Abs(5) = 5, Abs(0) = 0

1.5.2.1 Fallunterscheidungen in VB.NET

Die Fallunterscheidung wird mit dem IF … THEN- Block implementiert. Zwischen IF … THEN wird die Bedingung definiert. Wenn sie zur Laufzeit erfüllt wird, dann werden die im Block definierten Berechnungen ausgeführt, sonst nicht.

  ' VB.NET
Public Function ABS(z)
  If z < 0 Then
    Return -z
  ElseIf z > 0 Then
    Return z
  Else
    Return 0
  End If
End Function

1.5.2.2 Übung

Implementieren Sie eine Funktion in VB.NET mit zwei Parametern, die den Preis eines Wirtschaftsgutes und der Warengruppe, zu der es gehört, auf einen Mehrwertsteuerbetrag abbildet:

  Mehrwertsteuerberechnung
                               preis * 0,07       wg = "ermäßigt"
MwSt(preis, wg): (preis, wg) →               für
                               preis * 0,19       wg = "normal"

1.5.3 Funktionen hintereinander schalten

In der Mathematik kann man mittels Funktionen eine Menge {x} auf eine Menge {y}, und die Menge {y} wiederum auf die Menge {z} abbilden. Formal wird dies wie folgt beschrieben:

Wenn f1(x): x → y und f2(y): y → z dann ist f2(f1(x)) : x → z

Damit kann man z.B. die Berechnungen des Kreisumfanges an Durchmesser anpassen, die in verschiedenen Maßeinheiten gemessen wurden:

  
    Funktionen hintereinanderschalten
  

MillimeterInMeter(x): x → x * 0,001
CentimeterInMeter(x): x → x * 0,01

Kreisumfang(D): D →  π * D

                     D                           D
            /--------+--------\             /----+----\   
Kreisumfang(MillimeterInMeter(x)): x →  π * (x * 0,001)
Kreisumfang(CentimeterInMeter(x)): x →  π * (x * 0,01)

z.B. Kreisumfang(CentimeterInMeter(100)) = 3,142..., Kreisumfang(MillimeterInMeter(100)) = 0,3142...

1.5.3.1 Funktionen hintereinanderschalten in VB.NET

Die Syntax der Hintereinanderschaltung ist identisch mit der bereits eingeführten allgemeinen Syntax.

  
    Dim UmfangInMeter = Kreisumfang(MillimeterInMeter(x))
  

1.5.3.2 Übung

Zu einem Netto- Preis soll erst der Brutto- Preis (inkl. MwSt) und dann der Skonto (3%) berechnet werden. Implementieren Sie die Stufenweise Preisermittlung durch Hintereinanderschalten von Funktionen.

1.5.4 Rekursion

Mathematiker sind findige Menschen. Sie hatten schnell erkannt, dass mit der Hintereinaderschaltung von Funktionen ein mächtiges Instrument bereitsteht, mit dem man sich weit in die Tiefen mathematischer Probleme vorwagen kann. Eine besondere Form ist die Rekursion. Auf dieser basieren die funktionalen Beschreibungen vieler mächtiger Algorithmen.

Z.B. soll ein Vertreter 3 Kunden nacheinander besuchen, wobei die gewählte Reihenfolge die mit der kleinsten Gesamtstrecke ist (Problem der Rundreise mit minimaler Streckenlänge).

Die Lösung benötigt zunächst einen Graphen, dessen Knoten den Startpunkt und die Kunden darstellen, und dessen Kanten die Entfernungen zwischen den Knoten angeben. Dann sind für alle möglichen Reihenfolgen der Besuche die Summen der Wege zu berechnen. Am Ende ist die Reihenfolge mit der kleinsten Summe als Lösung auszuwählen. Folgendes Bild veranschaulicht das Vorgehen:


Wie viele Reihenfolgen über dem Graphen, und wie viele Summen damit gebildet werden müssen, gibt die aus der Kombinatorik bekannte Fakultät- Funktion an:

  Fakultät berechnen

                   1     n = 0             
N!(n): n →           für 
           n*N!(n-1)     n > 0

Die Funktion wird mittels einer Fallunterscheidung definiert, wobei für den Fall n > 0 der Funktionswert durch einen Term beschrieben wird, der N! wiederum enthält. Man erhält eine Lösung, indem man auf eine Lösung für den Vorgänger zurückgreift. Diese Art von Funktionsdefinition wird Rekursion genannt.

Aufgelöst wird die Rekursion von N! z.B. für n = 3 wie folgt:

  
    N!(3) = 3*N!(2) = 3*2*N!(1) = 3*2*1*N!(0) = 3*2*1*1 = 6
  

Die Fakultät wächst mit n extrem. Die exakte Lösung des Rundreiseproblem für große n praktisch unmöglich.

1.5.4.1 Rekursion in VB.NET

  Public Function Fact(n)
  If n = 0 Then
     Return 1
  Else
     Return n * Fact(n - 1)
  End If
End Function

1.5.4.2 Übung

Implementiere eine rekursive Funktion, die eine gegebene Zahlenpaar Basis, Exponent die Potenz berechnet wie folgt:

Potenz(Basis, Exponent): (Basis, Exponent) → Basis Exponent

Beachte dabei die Beziehung: b e = b * b e-1 = b * b * b e-2

1.5.5 Listenverarbeitung

In den Naturwissenschaften werden oft mehrere Messwerte einer Beobachtung zugeordnet werden. Es entstehen Paare von Messwerten. Ein Beispiel ist die Beobachtung eines beschleunigenden Fahrzeugs. Dabei wird in festen Zeitabständen die Momentangeschwindigkeit notiert.

  
    Messung: {(v, t)} := {(1 m/s, 1s), (2 m/s, 2s), …, (12 m/s, 5s)}
  

Ein anderes Beispiel sind die möglichen Folgen, in denen die Kunden A, B, C besucht werden (siehe vorausgegangener Abschnitt)

Besuchsreihenfolgen: {(K1, K2, K3)} := {(A, B, C), (B, A, C), (B, C, A), (C, B, A), (C, A, B), (A, C, B)}

Die Permutationen über der dreielementigen Menge {A, B, C} bilden hier alle möglichen Reihenfolgen. Jede Reihenfolge wird als ein Triple dargestellt.

Paare, Tripel oder allgemein Tupel sind spezielle Formen von Listen.

  
    Liste := (e1, e2, …., eN)
  

Mit Listenverarbeitung werden allgemein Funktionen bezeichnet, die Listen auf Listen oder Listen auf skalare Werte abbilden

  
    Listenverarbeitung := {f mit f(Liste X) : Liste X → Liste Y oder f(Liste X): Liste X → Y}
  

1.5.5.1 Listenverarbeitung in VB.NET

Anstelle runder Klammern werden in VB.NET für die Listenbegrenzer geschweifte Klammern verwendet. Auch die Definition eines Listenbezeichners verwendet statt := das Symbolpaar Dim und =.

  
    Dim Liste = { e1, e2, …, eN}
  



Folgenden Tabelle definiert die grundlegenden Funktionen der Listenverarbeitung

Name

Beispiel

Beschreibung

{...}

Dim ListeX = {1,2,3}

Erzeugt eine Liste mit den angegebenen Elementen.

{…}(i)

2 = {1,2,3}(1)

Liefert den Wert des i-ten Listenelementes

Count({…})

3 = Count({1,2,3})

Gibt die Anzahl der Elemente in einer Liste zurück

First({…})

1 = First({1,2,3})

Gibt das erste Element einer Liste zurück

Last({…})

3 = Last({1,2,3})

Gibt das letzte Element einer Liste zurück.

Reverse({…})

{3,2,1} = Reverse({1,2,3})

Liefert eine Liste mit allen Elemente der Eingangsliste in umgekehrter Reihenfolge

Take({…}, i)

{1,2} = Take({1,2,3,4}, 2)

Liefert eine Liste mit den ersten i Elemente der ursprünglichen Liste

Skip({…}, i)

{3,4} = Take({1,2,3,4}, 2)

Liefert eine Liste mit den ersten i Elemente der ursprünglichen Liste

Concat({…},{…})

{1,2,3,4,5} = Concat({1,2,3},{4,5})

Verkettet zwei Listen zu einer neuen Liste

ForEach({…}, f)

{1,4,9} = ForEach({1,2,3}, Function(x) x*x)

Liefert zu einer Liste die Liste mit den Funktionswerten f(x) für jedes x aus der eingegebenen Liste.



Im Folgenden Beispiele für einfache Funktionen zur Listenverarbeitung. Wie man sieht, wird die Rekursion intensiv genutzt:

Bildet die Summe aller Werte in einer Liste

  
    Summenbildung
  

Summe({a1, a2, …, aN}): {a1, a2, …, aN} → a1 + a2 + … + aN

Function Summe(liste)
  If Count(liste) = 0 Then
     Return 0
  ElseIf Count(liste) = 1 Then
     Return liste(0)
  Else
     Return First(liste) + Summe(Skip(liste, 1))
  End If
End Function

Sucht das Minimum in einer Liste:

  Minima finden

Min({…}): {…} → min mit min ϵ {…} & Für alle x ϵ {…} gilt: min <= x

Function Min(list)
   If Count(List) = 0 Then
      Throw New Exception()
   ElseIf Count(List) = 1 Then
      Return List(0)
   ElseIf First(list) < Min(Skip(list, 1)) Then
      Return First(list)
   Else
      Return Min(Skip(list, 1))
   End If
End Function

Erzeugt eine Liste mit den Werten 1-N:

  Liste mit Werten erzeugen

PTupelStart(n): n → {1,2, …,n}

Function PTupelStart(n)
   If n = 1 Then
      Return {1}
   Else
      Return Concat(PTupelStart(n - 1), {n})
   End If
End Function

Prüft zwei Listen auf vollständige Übereinstimmung:

  
    Gleichheit von Listen prüfen
  

Equal({a1,a2,…,aN}, {b1,b2,…,bM}): ({a1, a2, …, aN}, {b1, b2, …, bM}) → true für 
                                         Count({a1,a2,…,aN})  = Count({b1, b2, …, bM}) und
                                         a1 = b1, a2 = b2, …, aN = bM

Function Equal(listA, listB)
  If Count(listA) <> Count(listB) Then
     ' Ungleich lange Listen können nie gleich sein
     Return False
  ElseIf Count(listA) > 0 Then
     ' Rückführung auf die Prüfung, das der Anfang gleich ist und der Rest auch
     Return First(listA) = First(listB) And Equal(Skip(listA, 1), Skip(listB, 1))
  Else
     ' Zwei leere Listen sind immer gleich
     Return True
  End If
End Function

1.5.6 Primzahlscanner funktional realisieren

Teilaufgabe 1 kann z.B. durch folgende Funktion dargestellt werden (Pseudokode):

  
    IstPrimzahl(z): z → z Mod 2 <> 0 AND TesteWeiterAufPrimzahl(z, 3)
  

TesteWeiterAufPrimzahl(z, t): z,t → z Mod t <> 0 AND
                                    Wenn t = z/2 
                                    dann true 
                                    sonst TesteWeiterAufPrimzahl(z, t + 1)

Wir setzen die Lösung in VB.NET um:

  Function IstPrimzahl(z As Long) As Boolean
  Return TesteAufTeiler(z, 2)
End Function

Private Function TesteAufTeiler(ByRef z As Long, KandidatTeiler As Long) As Boolean
  If KandidatTeiler >= z \ 2 + 1 Then
    Return True
  Else
    Return z Mod KandidatTeiler <> 0 And TesteAufTeiler(z, KandidatTeiler + 1)
  End If
End Function

Die funktionale Lösung ist eine, dem Mathematiker geläufige Form und besticht durch ihre Kürze. Nicht- Mathematikern haben aber in der Regel große Probleme, die Wirkung dieser funktionalen Ausdrücke, die auf Rekursion beruht, zu verstehen.

Auch für Teilaufgabe 2 gibt es eine Funktionale Lösung (Pseudokode):

  
    AllePrimzahlenIn(a, b): a, b → Alle x in [a, b] für die gilt: IstPrimzahl(x)
  

Die Funktion bildet dabei das Paar a,b auf eine Menge mit allen Primzahlen zwischen a und b ab.

In VB.NET kann die Lösung mittels LINQ To Objects ab .NET 3.5 direkt umgesetzt werden

1.6 Erweiterte Grundlagen

1.6.1 Modifikatoren

Modifikatoren beeinflussen das Laufzeitverhalten einer Variable. So wird durch einen Const- Modifikator das Ändern des Inhaltes einer Varaible zur Laufzeit verboten. Mit dem Static- Modifikator wird die Platzierung der Variable in einem globalen Segement erreicht, wodurch sie mit Programmstart erzeugt wird und für die gesamte Dauer des Programms erhalten bleibt.



Modifikator

Beschreibung

Anwendbar auf

Const

Inhalt der Variable ist für die gesamte Ausführungszeit unveränderlich

Modulebene

ReadOnly

Nur in der Dekalration oder im Konstruktor einr Klasse kann ein Wert an einen ReadOnly zugewiesen werden

Modulebene

Static

Definiert eine lokale Variable innerhalb einer Prozedur oder Funktion, die für den gesamten Zeitraum des Programmablaufes gültig, aber nur innerhalb der Methode sichtbar ist.

Prozedurebene

Shared

Definiert einen Klassenmember. Der Member ist unabhängig von Instanzen der Klasse. Entspricht den Modulvariablen oder Prozeduren von VB6

Modulebene

1.6.2 Zugriffsmodifikatoren

Durch Zugriffsmodifikatoren wird das Prinzip der Kapselung in VB realisiert. Über Zugriffsmodifikatoren wird zur Entwurfszeit gesteuert, von welchen Programmteilen aus auf eine Variable zugegriffen werden kann. Dadurch können Implementierungsdetails von Bibliotheken verborgen werden. Sinn macht die Diskussion der Zugriffsmodifikatoren erst im Zusammenhang mit Klassen.

Zugriffsmodifikator

Beschreibung

Public

Öffentlicher Member. Auf ihn kann aus jeder Ebene zugegriffen werden.

Private

Kennzeichnet ein Implementierungsdetail. Ist außerhalb des Klassen oder Modulblockes, in dem er definiert wurde, nicht sichtbar

Protected

Kennzeichnet ein Implementierungsdetail einer Klassenhierarchie. Ist außerhalb der Klassenblöcke der Klassenhierarchie nicht sichtbar

Friend

Zugriff nur innerhalb der Assembly möglich, in der der Member deklariert wurde

1.6.3 Reflektion

Die in den Type- Objekten gespeicherten Metainformationen beschreiben die Struktur einer Klasse vollständig. Beispielsweise können alle Member einer Klasse wie folgt aufgelistet werden:

Imports System
Imports System.Reflection


class CTest 
  Dim ganzZahl as Integer
  enum EPartei
       gut
       boese
       gerecht
  End Enum
  Function TueWas() as Integer
     ganzZahl += 1
     Return ganzZahl
  End Function
End Class

:
    Sub Main()

        Dim t As Type

        t = GetType(CTest)

        ' Alle Felder, Eigenschaften und Methoden werden aufgelistet                 

        Dim minfos() As MemberInfo = t.GetMembers()

        For Each minfo As MemberInfo In minfos
            Console.WriteLine("{0}", minfo.ToString())
        Next

    End Sub

1.6.3.1 Aufgaben

  1. Entwickeln Sie eine Klasse CTypeBrowser, welche die Methode info(object) implementiert, die zu einem gegebenen Objekt alle Felder und Methoden auflistet.

1.6.4 Elementare Ein/Ausgabe

Beispiel:

Dim preis As Dezimal = 99.45
Console.WriteLine("Preis: {0,10:########.##}€", preis)

1.6.4.1 Formatzeichenfolgen

Über Formatzeichenfolgen wird abhängig vom Datentyp die Darstellung eines Wertes als String gesteuert. Es gibt Formatzeichenfolgen für:

1.6.4.2 Formatzeichenfolgen für nummerische Typen

Formatzeichenfolgen bestehen aus einem Formatbezeichner und optional aus der Angabe einer Genauigkeit XX. Um eine Zahl mit max. 3 Nachkommastellen auszugeben, kann folgende Formatzeichenfolge verwendet werden:

N3

Folgende Formatbezeichner sind vordefiniert:

Bezeichner

Bedeutung

Beispiel

Resultat

C

Währungsbetrag

(99.490).ToString("C")

99,49 €

D

Ganzzahliger Wert. Die Genauigkeit definiert die minimale Ausgabebreite. Sollte die darzustellende Zahl die minimale Ausgabebreite nicht erreichen, dann wird mit führenden 0-en aufgefüllt

(123).ToString("D5")

00123


E

Zahl in Exponentialschreibweise

(99.49).ToString("E")

9,95E+005

F

Festkomma. Die Genauigkeit gibt eine feste Anzahl von Nachkommastellen an

(99.49).ToString("F1")

99,5

G

Allgemein- Formatierung erfolgt abhängig vom Typ der Zahl

(99.49).ToString("G")

99,5

N

Zahl (allgemein). Die Genauigkeit beschreibt die Anzahl der Nachkommastellen

(99.49).ToString("N")

99,49

P

Prozentzahl. Der Wert wird mit 100 multiplizieret und mit einem Prozentzeichen versehen

(0.15).ToString("P")

15,00%

H

Hexadezimal

(0xA1).ToString("H")

A1

R

Stellt Zahlen so als Zeichenkette dar, daß sie aus diesen ohne Genauigkeitsverlust wieder zurückkonvertiert werden können.

(99.49).ToString("R")

99,49

Neben vordefinierten Formatzeichenfolgen können Formate wie im obigen Beispiel auch selbst definiert werden. Siehe dazu: Benutzerdefinierte Formatzeichenfolgen

1.6.4.3 Formatzeichenfolgen für Datumstypen

Bezeichner

Bedeutung

Beispiel

Resultat

d

Kurzes Datumsformat

DateTime.Now.ToString("d")

27.04.06

D

Langes Datumsformat

DateTime.Now.ToString("D")

27. April 2006

T

Zeitformat

DateTime.Now.ToString("T")

12:17:59

s

ISO 8601 konformes Datumsformat. Werte sind sortierbar. Dieser Typ wird z.B. in XML- Schema angewendet

DateTime.Now.ToString("s")

2006-04-27T12:17:47

1.6.4.4 Formatzeichenfolgen für Aufzählungstypen

Bezeichner

Bedeutung

Beispiel

Resultat

G

Stellt Wert eines Aufzählungstyps als String dar, falls möglich. Andernfalls wird der Wert als Integer- Zahlenwert dargestellt

(SUnits.cm).ToString()

"SUnits.cm"

F

Stellt Wert eines Aufzählungstyps als String dar, falls möglich. Andernfalls wird der Wert als Integer- Zahlenwert dargestellt

(SUnits.cm).ToString()


D

Stellt Wert eines Aufzälungstyps als dezimalen Integer dar

(SUnits.cm).ToString()

2

X

Stellt Wert eines Aufzälungstyps als hexadezimalen Integer dar

(SUnits.cm).ToString()

0x2

1.6.4.5 ToString und Formatierung




1.6.4.6 Ausgabe mit IFormatProvidern

Imports System.Globalization;

' Kopie des aktuell wirksamen Formatproviders erzeugen
Dim nif as NumberFormatInfo 
nif = CType(System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.Clone(),  NumberFormatInfo)

' Währungssysmbol auf Dollar umstellen
nif.CurrencySymbol = "$"

' Ausgabe eines Währungswertes
Dim preis as Double
preis = 99.99
Console.WriteLine(preis.ToString("C", nif))

1.6.4.7 Composit- Formating

In Methoden wie Console.WriteLine("Formatstring", object, ...) können Textkonstanten und Formatblöcke, die Formatzeichenfolgen für auszugebende Parameter enthalten, kombiniert werden. Dies wird als Composit- Formating bezeichnet. Soll z.B. ein Längenmaß ausgegeben werden, welches aus dem Wert und der Längeneinheit besteht, dann könnte dies durch folgende Anweisung erfolgen:

Console.WriteLine("Längenmaß: {0,5:N} {1,2}", LMasz.value, LMasz.unitToString());

Ein Formatblock hat folgende Syntax:

{ParamListenIndex[,alignment][:Formatzeichenfolge]}

Der ParamListenIndex bezeichnet den Parameter aus der Parameterliste, dessen Wert durch den Parameterblock formatiert werden soll. Das Alignment definiert die Ausrichtung und mindestbreite. Positive Werte stehen für eine Rechtsausrichtung, negative für eine Linksausrichtung.

1.6.4.8 Array mit selbstdefinierten Typen Sortieren

enum ELaenge {mm, cm, dm, m, km};

struct Laenge : IComparer
{
  public float  wert;
  public enumLaengenEinheiten einheit;

  public int Compare(object obj1, object obj2) 
  {
    Laenge l1 = (Laenge)obj1;
    Laenge l2 = (Laenge)obj2;

    // alles in mm umrechnen
    float il1 = tomm(l1), il2 = tomm(l2);
    if (il1 == il2)
      return 0;
    else if (il1 < il2)
      return -1;
    else 
      return 1;         
  }

  float tomm(Laenge l) 
  {
     switch (l.einheit) {
         case ELaenge.mm:
            return l.wert;
         case ELaenge.cm:
            return l.wert * 10;
         case ELaenge.dm:
            return l.wert * 100;
         case ELaenge.m:
            return l.wert * 1000;
         case ELaenge.km:
            return l.wert * 1000000;
     }
     return -1;
  }
}
// In Main
Laenge[] fahrten = new Laenge[3];
fahrten[0].einheit = ELaenge.mm;
fahrten[0].wert = 100000;
fahrten[1].einheit = ELaenge.cm;
fahrten[1].wert = 3000;
fahrten[2].einheit = ELaenge.km;
fahrten[2].wert = 0.99F;

Array.Sort(fahrten, fahrten[0]);

foreach (Laenge fahrt in fahrten) 
{
   Console.WriteLine("{0} {1}", fahrt.wert, fahrt.einheit.ToString());
}

1.6.4.9 Aufg.:

  1. Min/Max- Suche,

  2. Sortieren

  3. Messwerterfassung (akt. Wert, dyn. Mittelwert)