© Martin Korneffel, Stuttgart 2005 +++ email: trac@n.zgs.de +++ web: www.s-line.de/homepages/trac
Objekt |
Ein Objekt steht für ein konkretes Ding (z.B. Der rote Ferrari von Fred Vollgas). In C# sind sind Objekte Bündel aus speziellen Daten und Prozeduren, die über eine gemeinsamme Hauptspeicherreferenz erreichbar sind (z.B. FredsFerrari.Farbe oder FredsFerrari.gasGeben(100);) In der objektorientierten Sichtweise werden die Prozeduren eines Objektes als Methoden, und die Daten als Elemente bezeichnet. |
Durch die Einführung des Objektbegriffes in C# können Systeme mit komplexer innerer Struktur direkt beschrieben werden. Jedes Objekt wird mit einem Namen gekennzeichnet, jeder Prozess in einem Objekt durch eine sogenannte Methode ausgedrückt, und jeder Zustand durch eine Eigenschaft dokumentiert. Beziehungen und Prozesse, an denen mehrere Objekte beteiligt sind, können durch Methoden, die Objekte als Parameter erwarten, und Eigenschaften, die wiederum Objekte sind, ausgedrückt werden.
Aufgaben:
Beschreiben Sie folgende Systeme durch Objektmengen. Stellen Sie die Objekte durch Objektdiagramme dar.
Bankkonto
Geometrische Figuren
Klasse |
Eine Klasse ist eine Menge von Objekten, die einen gemeinsammen strukturellen Aufbau aus Daten und Prozeduren haben. Die Menge kann durch eine Klassendeklaration beschrieben werden. Eine Klassendeklaration listet Deklarationen von Datenelementen und Prozeduren auf, die die Objekte der Klasse enthalten. |
In VB.NET werden Klassen durch
Konstruktoren |
Sind vom Anwender überschreibbare Prozeduren, die unmittelbar nach Allokation des Objektes der Klasse auf dem Heap gestartet werden. Konstruktoren können wie normale Prozeduren Eingaben verarbeiten, jedoch können sie keine Ausgaben vornehmen ! In VB.NET werden Konstruktoren in Methoden mit dem Namen New(...) implementiert. New kann mehrfach überladen sein. Class Lager public shared instancecounter as Integer = 0 // Konstruktor public Sub New(pKapzitaet as Integer) instancecounter += 1 End Sub : End Class |
Um statische Klassenkomponenten zu initialisieren gibt es spezielle Konstruktoren, genannt statische Konstruktoren. Diese werden beim erstmaligen gebrauch einer Klasse aufgerufen:
class C { public static int anz_instanzen; static C() { anz_instanzen = 1; } }
Destruktoren |
Spezielle Funktionen einer Klasse, die immer unmitterlbar vor dem Löschen eines Objektes durch den GC starten. Entspricht der Finalize – Methode von System.Object. |
Definition |
|
---|---|
Dispose- Methode |
Jede Klasse kann die Schnittstelle IDisposable implementieren, welche die Methode Dispose() deklariert. Dispose wird in einer Klasse implementiert, um sofort Resourcen freizugeben, wenn das Objekt nicht mehr benötigt wird. Hat die Dispose- Methode bereits alle Aufräumarbeiten erledigt, dann kann der explizite Aufruf des Destruktors durch den GC unterdrückt werden wie folgt: GC.SuppressFinalize(Me); |
Es gibt Methoden und Membervariablen, die nicht an eine spezielle Instanz gebunden sind. Beispielsweise kann eine Klasse SVal zur Darstellung von Wegmaßen eine Methode zur Umrechnung von einem Wegmaß in ein anders wie folgt enthalten:
' Klase zur Darstellung von Wegmaßen Class SVal Public value As Double Public unit As SUnits Public Function from_to(ByVal fromUnit As Sunits, ByVal toUnit As Sunits,_ ByVal value As Double) As Double : End Class
Die Funktion gehört zum Kontext Wegmaß, jedoch wäre eine konkrete Bindung an ein Wegmaß- Objekt sinnlos. Deshalb kann sie als shared Member mittels des Zugriffsmodifizierers Shared deklariert werden und kann schließlich über den Klassennamen aufgerufen werden:
' Klase zur Darstellung von Wegmaßen Class SVal Public value As Double Public unit As SUnits ' Folgende Funktion ist an keine Instanz (Wegmaß) gebunden. ' Mit ihr können Wegwerte von einer Einheit in eine andere ' umgerechnet werden. Public Shared Function from_to(ByVal fromUnit As Sunits,_ ByVal toUnit As Sunits,_ ByVal value As Double) As Double : End Class Sub Main() : Dim weg_in_km as Double = SVal.from_to(SUnits.s_mm, SUnits.s_km, 99) : End Sub
Eigenschaften sind Objektzustände, deren Zugriff über Get/Set Methoden gesteuert wird.
Class CConnectionString Dim Server as String Dim Database as String : public Property conString as String Get return "Server=" + Host + ";" + "Database=" + Database End Get Set(ByVal Value as String) ' String in die Bestandteile aufsplitten und Werte den ' Membervariablen zuweisen End Set End Property End Class
class CAuto : Public vx as Single Public Event Bremsen() Public Sub fahren(pvx as Single, t as Single) if pvx < vx Then RaiseEvent Bremsen() End If End Sub : End Class
Module M1 Sub OnBremsen() Console.WriteLine("Quietsch") End Sub Sub Main() Dim bmw as New CAuto AddHandler bmw.Bemsen, AdressOf OnBremsen End Sub End Module
Konstanten sind Felder, deren Wert zur Kompilationszeit definiert wird, und die anschließend nur über den Klassennamen referenzierbar sind.
const e as Double = 2.72;
Readonly- Member sind Felder, deren Wert zur Konstruktionszeit definiert werden. D.h. ihnen können im Konstruktor Werte zugewiesen werden. Zu späteren Zeitpunkten ist keine Zuweisung mehr möglich.
class Figur ReadOnly max_anz_leben as Integer Public Sub New(max_al as Integer) max_anz_leben = max_al End Sub Public Sub ueberleben() max_anz_leben += 1 ' Fehler, da readonly End Sub : End Class
Bei der objektorientierten Programmierung fällt auf, das verschiedene Klassendekalrationen in einem Projekt Abschnitte mit identischen Deklarationen von Eigenschaften und Methoden enthalten können. Hier kann es sinvoll sein, diese Gemeinsamkeiten zentral an einer Stelle im Programm zu deklarieren zwecks Erhöhung der Übersichtlichkeit und Vereinfachung der Wartung.
Ein anderer Aspekt ist die Verwaltung von Objekten unterschiedlicher Typen in einer Liste wie z.B. bei der programmtechnischen Darstellung von CAD- Zeichnungen (Menge aus Linen, Kreisen etc). Ein Ausdruck der Zeichnung wird durch einen Durchlauf der Liste realisiert, wobei von jedem Objekt die draw- Methode aufgerufen wird.
Technische 2D Zeichungen sind aus elementaren geometrischen Primitiven wie Linien, Rechtecke, Kreise, Ellipsen, Splines etc.. Es bietet sich in bietet sich an, den Entwurf damit zu beginnen, für jedes Primitiv eine Klasse zu deklarieren:
Wie
aus dem Diagramm ersichtlich, haben alle Klassen einen Satz
gemeinsamer Eigenschaften und Methoden:
Color, zur Darstellung der Linienfarbe
style, zur Darstellung der Strichart
unit, zur Darstellung der Einheit, in der gezeichnet wird
Methode draw zum Zeichnen der Figur auf einem Ausgabemedium
Methoden translate, rotate und scale zum Verschieben, Drehen und Strecken der Figur
Die Eigenschaften color, style und unit sind in allen Klassen die gleiche. Sie können in einer gemeinsammen Basisklasse aller Primitivklassen zentral deklariert werden. Nennen wir diese Klasse CFigur. Durch Vererbung werden die von CFigur abgeleiteten Klassen mit den Eigenschaften von CFigur ausgestattet:
Die
Daklaratioen der Methoden draw, translate, rotate und scale
können nicht einfach in die Basisklasse CFigur verschoben
werden. Denn für jedes Klasse zu einem Grafikprimitv ist eine
individuelle Implentierung diesser notwendig.
Die Vererbung wird in der Abgeleiteten Klasse deklariert mittels des Schlüsselwortes Inherits:
Public Class CLinie Inherits CFigur : End Class
Bei der Vererbung kann es schnell zu Namenskonflikten kommen. Beispielsweise implementieren CLinie und CFigur jeweils einen Konstruktor New(). Zur korrekten Instanziierung eines CLinie- Objektes ist es notwendig, daß auch der Konstruktor der Basisklasse CFigur aufgerufen wird. Hier ist eine Unterscheidung im Kontext der abgeleiteten Klasse zwischen eigenen Membern und der der Basisklasse nötig. Dies geschieht durch das Schlüsselwort MyBase
Public Class CLinie Inherits CFigur : Public Sub New() MyBase.New() ' Aufruf des Konstruktors aus CFigur End Sub : End Class
Manchmal ist es notwendig, den Typ einer Eigenschaft oder Methode in einer abgeleiteten Klasse neu zu definieren. Dies geschieht durch Überschatten (engl,. Shadowing) und wird in VB.NET durch Schlüsselwort Shadows gekennzeichnet:
Class CFestung Public mAlter As Int32 Public Sub setAlter(ByVal alter_neu As Double) mAlter = CType(alter_neu, Int32) End Sub End Class Class CBurg Inherits CFestung Shadows mAlter As Double End Class
In der Klassenbibliothek für 2D- Zeichnungen wurde für jedes Primitiv ein Satz von Transformationsfunktionen definiert (translate, rotate, scale). Nehmen wir die Translationsfunktion. Sie könnte zu. Beispiel wie folgt definiert werden:
Sub translate(tx as Single, ty as Single)
Anstelle von tx und ty könnte aber auch ein Punkt übergeben werden, der das Ende des Verschiebevektors kennzeichnet:
Sub translate(vec as SPoint)
Um die erste, als such zweite Variante zu ermöglichen, muß das Schlüsselwort Overloads eingesetzt werden (insbesondere bei Vererbung)
Overloads Sub translate(tx as Single, ty as Single) Overloads Sub translate(vec as SPoint)
Bei der Entwicklung großer Programmpakete hat es sich als sinvoll erwiesen, Implementierungsdetails von Bibliotheken vor dem Anwender zu verbergen. In VB.NET wird dieses sog. Kapselungsprinzip durch Zugriffmodifikatoren für Klassenmember und Schnittstellen realisiert.
Zugriffsmodifikator |
Beschreibung |
Nicht deklarierebar in |
---|---|---|
Dim |
nur im Block sichtbar, in dem deklariert wurde |
|
Public |
überall sichtbar. Über Public- Methoden und Eigenschaften werden an die Objekte der Klasse Nachrichten gesendet. |
Prozeduren, Funktionen |
Private |
nur in der Klasse sichtbar, in der Member deklariert wurde. Zur Kapselung von Implementationsdetails in einer Klasse |
Prozeduren, Funktionen |
Protected |
nur von Membern der eigenen oder von Membern in abgeleiteten Klassen sichtbar. Zur Kapselung von Implementationsdetails in einer Klassenhierarchie |
Prozeduren, Funktione, Module |
Friend |
(analog internal in C#) ist überall innerhalb der Assembly sichtbar. Außerhalb der Assembly nicht sichtbar. Zur Kapselung von Implementationsdetails in einer Assembly |
Prozeduren, Funktionen |
Module Module1 Class CFestung Public Shared mShared As Int16 Private mPrivat As Short Protected mProtected As Int16 Friend mFriend As Int16 Public mPublic As Int16 Sub New(ByVal init As Short) mShared = init mPrivat = init + 1S mProtected = init + 2S mFriend = init + 3S mPublic = init + 4S End Sub Public Sub tor_auf() ' Auf Private Elemente kann nur in Mathoden aus der Klasse ' selbst zugegriffen werden mPrivat = 1000 End Sub ' Ein Ereignis deklarieren Public Event treffer() ' Das Ereignis selbst auslösen Public Sub getroffen() RaiseEvent treffer() End Sub End Class ' Eine von Festung abgeleitete Klasse Class CBurg Inherits CFestung ' Konstruktoren werden nicht vererbt. Jede Klasse hat ihren ' eigenen Satz von Konstruktoren (Initialisierungsroutinen) Sub New(ByVal init As Int16) MyBase.New(init) ' Auf Protected- Member aus der Basisklasse kann in der abgeleiteten Klassen ' zugegriffen werden mprotected *= 10S End Sub Function gebe_protected_aus() As Int16 Return mprotected End Function End Class ' Ein Eventhandler für Objekte vom Typ Festung Sub festung_treffer() Console.WriteLine("Festung wurde getroffen") End Sub Sub Main() ' Eisenstein ist ein Objekt vom Typ CFestung Dim Eisenstein As New CFestung(10) Dim Dreistein As New CFestung(100) Dim Raubstein As New CBurg(1000) Console.WriteLine("Zuegriff auf Protected {0}", Raubstein.gebe_protected_aus()) ' Eventhandler registrieren AddHandler Eisenstein.treffer, AddressOf festung_treffer ' Zugriff auf die Member CFestung.mShared *= -1S Eisenstein.mShared *= 99S ' Nur shared Elemente können dierekt über die Klasse aufgerufen ' werden. Es mus kein Objekt/Instanz existieren, um mit dem ' Element zu arbeiten 'CFestung.mPublic *= 12S With Eisenstein '.mPrivat *= -1 '.mProtected *= -1S .mFriend *= -1S .mPublic *= -1S ' Ereignis auslösen .getroffen() .tor_auf() End With Console.ReadLine() End Sub End Module
Polymorphe Operation |
(Vielgestaltig) Sind Funktionen oder Prozeduren, die unter gleichem Namen und mit gleicher Parameterliste in verschiedenen Klassen deklariert, jedoch verschieden implementiert sind. Beim Aufruf über ein Objekt wird immer die Implementierung aus dem Typ des Objektes genommen. Erfolgt der Aufruf über eine Referenz vom Typ einer Basisklasse, dann wird zur Laufzeit die Implementierung über sog. virtuelle Funktionstabellen bestimmt. |
Beim
Instanziieren werden die vtabels so initilisiert, daß ihre
Einträge auf die korrekten Funktionen zeigen.
Im folgenden Beispiel wird das erzeugen von Arbeitsfortschrittsmeldungen in der Methode MakeProgressInfo gekapselt. Diese wird zu bestimmten Zeitpunkten beim Scannen eines Verzeichnisses aufgerufen, um den Client von DirTree über den aktuellen Arbeitsstand zu informieren. In abgeleiteten Klassen können detailiertere Informationen zum Arbeitsfortschritt benötigt werden. In diesem Falle muß von der Klasse DirTreeProgressInfo eine speziellere Arbeitsfortschrittmeldung abgeleitet werden. In einer Methode, welche MakeProgressInfo überschreibt, muß die abgeleitete Arbeitsfortschrittmeldung dann erzeugt werden.
Namespace DMS Public Class DirTreeVb '----------------------------------------------------------------------------- ' Member zur Ausgabe des Arbeitsfortschrittes ' Klasse mit Informationen über den Arbeitsfortschritt. ' Detailiertere Arbeitsfortschrittmeldungen müssen von dieser Klasse ' abgeleitet werden. Public Class DirTreeProgressInfo Inherits ProgressInfo Public CountAllDirs As Integer Public CountAllFiles As Integer Public Sub DirTreeProgressInfo(ByVal CountAllDirs As Integer, ByVal CountAllFiles As Integer) Me.CountAllDirs = CountAllDirs Me.CountAllFiles = CountAllFiles End Sub End Class ' Funktionszeigertyp von Handlern zur Behandlung des Arbeitsfortschritts- Event public delegate function DGEventProgress(DirTreeProgressInfo info) as Boolean ' Ereignis: Arbeitsfortschritt Public Event EventProgress As DGEventProgress ' virtuelle Methode ' Generator für Arbeitsfortschrittmeldungen: Kann in abgeleiteten Klassen ' überschrieben werden, um detailiertere Arbeitsfortschrittmeldungen, die von ' DirTreeProgressInfo abgeleitet sind, zu erzeugen Protected Overridable Function MakeProgressInfo() As DirTreeProgressInfo return new DirTreeProgressInfo(m_dir_count, m_file_count); End Function End Class End Namespace
Definition
einer Schnittstelle
Public Interface IPlotter Sub print_line(ByVal a As SPoint, ByVal b As SPoint) Sub print_circle(ByVal m As SPoint, ByVal radius As Double) End Interface
Implementierung einer Schnittstelle
Public Class CDxfPlotter Implements IPlotter Public Sub print_line(ByVal a As SPoint, ByVal b As SPoint, ByVal style As CStyle)_ Implements IPlotter.print_line : End Sub End Class
Fehlerbehandlung kann klassisch mittels dem Err Objekt (unstrukturiert) bzw. modern mit dem try ... throw ... catch Block erfolgen.
Throw new Exception("Fehlermeldung");
Try ' Programmcode, der Fehlerobjekte werfen kann Catch ex As FileNotFoundException ' Behandeln einer Speziellen Ausnahme Catch ex As Expception ex ' Behandeln einer allgemeinen Ausnahme Finally ' Anweisungen, die in jedem Fall ausgeführt werden End Try
Try Try Throw new Exception("Ex Innen") Catch ex As Exception Console.WriteLine(ex.Message) throw new Exception("Ex Catch") finally Console.WriteLine("Finally 2"); End Try Console.WriteLine("Nach InnerTry"); Catch ex As Exception Console.WriteLine(ex.Message); finally Console.WriteLine("Finally 1"); End Try
In natürlichen Sprachen werden mittels Attribute Eigenschaften von Substantiven festgelegt wie heißer Tee oder grüne Farbe. Analog werden durch Attribute in VB.NET zusätzliche Informationen zu Klassen, Prozeduren und Variablen hinterlegt. Diese Informationen können dann vom Compiler verarbeitet werden, oder werden als Metadaten in der Assembly gespeichert.
Beispiel:
Definieren von Metadaten ür die Assembly:
<Assembly: AssemblyTitle("GeoInfo")> <Assembly: AssemblyDescription("Zugriff auf Datenbank")> <Assembly: AssemblyCompany("mkoSoft")> : <Assembly: AssemblyVersion("1.0.*")>
Attribute sind Klassen, die von System.Attribute abgeleitet sind. Jedes Attribut ist selbst wieder mit dem Basisattribut AttributeUsage ausgezeichnet. Damit ergibt sich folgende Grundstruktur für eine Attributdeklaration:
<AttributeUsage(AttributeTargets.All)> Public Class CMyXAttribute Inherits Attribute Sub New(ByVal pName As String) Name = pName End Sub Public Name As String End Class
Allgemein sind mit Attributen beliebige Elemente in .NET wie Assembly, Klasse und Member attributierbar. Über den Parameter AttributeTargets in AttributeUsage kann die Menge der attributierbaren Elemente eingeschränkt werden.
Die Attributierung eines Elements erfolgt beispielsweise so:
<CMyX("Martin")> Public Class CTest End Class
In der Parameterliste der Attributdefinition können Felder in einer Kommaseparierten Liste dierekt Werte zugewiesen werden.
Die Klasse System.Attribute stellt statische Methoden Attribute.GetCustomAttribute(..) und Attribute.GetCustomAttributes(...) bereit. Diese liefern eine Referenz auf ein Attribut vom Typ object bzw. eine Referenz auf ein Array mit Attributen zurück. Als Eingaben erwarten die Funktionen das Type- Objekt der Instanz, deren Attribute ausgelesen werden soll, sowie den Typ des auszulesenden Attributes.
Module Module1 Sub Main() ' Fall: Die Klasse Anwendung besitzt nur ein Attribut vom Typ MyAttribute Dim myAtt As CMyXAttribute = CType(Attribute.GetCustomAttribute(GetType(CTest), GetType(CMyXAttribute)), _ CMyXAttribute) If Not myAtt Is Nothing Then Console.WriteLine("name= {0}", myAtt.name) End If End Sub End Module