Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 <<   zurück
Visual Basic 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual Basic 2005

Visual Basic 2005
1.233 S., mit 2 CDs, 59,90 Euro
Rheinwerk Computing
ISBN 3-89842-585-1
gp Kapitel 10 Einige wichtige .NET-Klassen
  gp 10.1 Die Klasse »Object«
    gp 10.1.1 Der Konstruktor
    gp 10.1.2 Die Methoden der Klasse »Object«
  gp 10.2 Die Klasse »String«
    gp 10.2.1 Das Erzeugen eines Strings
    gp 10.2.2 Unveränderliche »String«-Objekte
    gp 10.2.3 Die Eigenschaften von »String«
    gp 10.2.4 Die Methoden der Klasse »String«
    gp 10.2.5 Zusammenfassung der Klasse »String«
  gp 10.3 Die Klasse »StringBuilder«
    gp 10.3.1 Allgemeines
    gp 10.3.2 Die Kapazität eines »StringBuilder«-Objekts
    gp 10.3.3 Die Konstruktoren der Klasse »StringBuilder«
    gp 10.3.4 Die Eigenschaften der Klasse »StringBuilder«
    gp 10.3.5 Die Methoden der Klasse »StringBuilder«
    gp 10.3.6 Allgemeine Anmerkungen
  gp 10.4 Der Typ »DateTime«
    gp 10.4.1 Die Zeitspanne »Tick«
    gp 10.4.2 Die Konstruktoren von »DateTime«
    gp 10.4.3 Die Eigenschaften von »DateTime«
    gp 10.4.4 Die Methoden der Klasse »DateTime«
    gp 10.4.5 Die Klasse »TimeSpan«
  gp 10.5 Die Klasse »Array«
    gp 10.5.1 Das Erzeugen eines »Array«-Objekts
    gp 10.5.2 Die Eigenschaften eines »Array«-Objekts
    gp 10.5.3 Die Methoden der Klasse »Array«
    gp 10.5.4 Array-Elemente sortieren
  gp 10.6 Ausgabeformatierung
    gp 10.6.1 Formatierung mit der Methode »String.Format«
    gp 10.6.2 Formatierung mit der Methode »ToString«
    gp 10.6.3 Benutzerdefinierte Formatierung

Kapitel 10 Einige wichtige .NET-Klassen


Galileo Computing

10.1 Die Klasse »Object«  downtop

Alle Klassen in der Klassenbibliothek des .NET-Frameworks sind Mitglieder einer Klassenhierarchie, die sich über viele Verzweigungen in aufgabenspezifischen Bereiche gliedert. Alle Klassen, so tief sie auch im Dickicht dieser Hierarchie stecken mögen, lassen sich aber auf die gemeinsame Basisklasse Object zurückführen. Entwickeln Sie eine benutzerdefinierte Klasse, müssen Sie nicht explizit angeben, dass Ihre Klasse von Object abgeleitet ist – diese Ableitung geschieht implizit. Dass sich alle Klassen von Object ableiten, hat eine ganz wesentliche Konsequenz: Jeder Typ des Systems weist ein Minimum gemeinsamer Verhaltensweisen auf.


Galileo Computing

10.1.1 Der Konstruktor  downtop

Die Liste der Konstruktoren ist nicht sehr lang – es gibt nur einen, der parameterlos ist und von jeder abgeleiteten Klasse aufgerufen wird, wenn ein Objekt erstellt wird. Wie Sie wissen, erfolgt dieser Aufruf implizit.


Galileo Computing

10.1.2 Die Methoden der Klasse »Object«  toptop

Insgesamt sieben Methoden vererbt die Klasse Object an ihre Subklassen. Fünf dieser Methoden sind Public und damit öffentlich, die beiden anderen Methoden Protected und werden daher zwar vererbt, erlauben aber nur den Zugriff aus der erbenden Klasse heraus.

Sehen wir uns zunächst in Tabelle 10.1 alle Methoden in einem Überblick an.


Tabelle 10.1     Die Methoden der Klasse »Object«

Methoden Beschreibung
Equals Diese Methode vergleicht zwei Objektreferenzen und liefert einen booleschen Wert zurück, dem entnommen werden kann, ob die beiden Referenzen auf dasselbe Objekt zeigen.
Finalize Dient dazu, Ressourcen der Klasse freizugeben, wenn das Objekt zerstört wird.
GetHashCode Liefert einen objektspezifischen Identifizierer.
GetType Liefert die Referenz auf eine Type-Instanz zurück, die den Typ des aktuellen Objekts beschreibt.
MemberwiseClone Dupliziert die aktuelle Instanz und liefert die Referenz auf das Duplikat zurück.
ReferenceEquals Vergleicht zwei Objektreferenzen und liefert einen booleschen Wert zurück, dem entnommen werden kann, ob die beiden Referenzen auf dasselbe Objekt zeigen.
ToString Liefert den voll qualifizierenden Namen einer Klasse.

Referenzvergleiche mit »Equals« und »ReferenceEquals«

Die beiden Methoden Equals und ReferenceEquals sind sich per Definition sehr ähnlich. Es werden zwei Objektvariablen miteinander verglichen, um festzustellen, ob beide dasselbe Objekt im Speicher referenzieren:


Dim firstRef As New ClassA
Dim secondRef As ClassA
secondRef = firstRef
Console.WriteLine(Object.Equals(firstRef, secondRef))

In diesem Codefragment wird die Referenz firstRef der Variablen secondRef zugewiesen. Beide Referenzen zeigen auf dasselbe konkrete Objekt, was der Aufruf der Equals-Methode bestätigt: Es wird True ausgegeben, was als referenzielle Identität der beiden Objektvariablen zu interpretieren ist. In diesem Fall können Sie sogar Equals gegen ReferenceEquals austauschen, am Ergebnis wird sich nichts ändern.

Sehen wir uns die Definition dieser beiden Methoden an, von denen Equals überladen ist:


Public Overridable Function Equals(Object) As Boolean
Public Shared Function Equals(Object, Object) As Boolean
Public Shared Function ReferenceEquals(Object, Object) As Boolean

Die erste Variante der Equals-Methode ist als Instanzmethode, die zweite als Klassenmethode implementiert. Beachten Sie, dass die Instanzmethode Overridable gekennzeichnet ist und von jeder Klasse polymorph überschrieben werden kann. Die statische Equals-Variante ist nicht überschreibbar, ebenso die ähnlich lautende Methode ReferenceEquals. Damit ist auch garantiert, dass das Ergebnis des Aufrufs einer dieser beiden Methoden immer den Vergleich zwischen zwei Objektreferenzen liefert: Es ist True, wenn beide Referenzen auf ein dasselbe Objekt verweisen, andernfalls lautet das Ergebnis False.

Das Überschreiben von »Equals« und die Methode »GetHashCode«

Wollen Sie selbst die Equals-Methode überschreiben, sollten Sie immer eine wichtige Regel beachten:


Wird die Equals-Methode einer Klasse überschrieben, sollte auch die Methode GetHashCode überschrieben werden.


Schauen wir uns kurz die Definition dieser Methode an:


Public Overridable Function GetHashCode() As Integer

Was aber leistet die Methode GetHashCode, und wozu dient sie?

Ein Hashcode ist eine Zahl vom Typ Integer, die ein bestimmtes Objekt eindeutig identifiziert. Diese Zahl wird auch als Schlüssel (key) bezeichnet, der immer zusammen mit einem Wert, beispielsweise einer Referenz, in Erscheinung tritt. Das Schlüssel-Wert-Paar wird in einer Hashtabelle verwaltet.

Jedes .NET-Objekt ist durch einen eindeutigen Schlüssel identifizierbar. Was hat das aber mit der Empfehlung zu tun, dass Sie als Entwickler GetHashCode überschreiben sollten, wenn Sie die Methode Equals überschreiben?

Standardmäßig benutzen wir Equals zum Referenzvergleich. Es könnte, wie Sie oben schon gesehen haben, natürlich auch ReferenceEquals sein oder – und genau das ist der entscheidende Punkt – auch über den Hashcode verglichen werden. Das folgende Beispiel beweist das. Es enthält eine Klasse mit einem parametrisierten Konstruktor, dem ein Integer übergeben wird. In der Main-Methode wird sowohl über Equals als auch über GetHashCode ein Vergleich gezogen, um zu bestimmen, ob zwei Variablen dasselbe Objekt referenzieren.


' ---------------------------------------------------------
' Beispiel: ...\Kapitel 10\HashCode
' --------------------------------------------------------
Module Module1
Sub Main()
Dim obj1 As ClassA = New ClassA(5)
Dim obj2 As ClassA = obj1
' Vergleich auf Basis des Hashcodes
If (obj2.GetHashCode() = obj1.GetHashCode()) Then
Console.WriteLine("Hashcode: Objekte sind gleich")
End If
' Vergleich auf Basis der Equals-Methode
If (obj2.Equals(obj1)) Then
Console.WriteLine("Equals: Objekte sind gleich")
End If
Console.ReadLine()
End Sub
End Module
Class ClassA
Public MyProp As Integer
Public Sub New(ByVal i As Integer)
MyProp = i
End Sub
End Class

Die Ausgabe ist wie erwartet: Über beide Methodenaufrufe wird die Gleichheit festgestellt, da mit der Zuweisung der Objektvariablen obj1 an obj2 auch der Hashcode des Objekts obj1 gleichermaßen Berücksichtigung findet – wie alle anderen Felder des Objekts.

Equals vergleicht zwei Referenzen. Dem Überschreiben dieser Methode könnte die Idee zugrunde liegen, andere Vergleichskriterien für den betreffenden Typ heranzuziehen, beispielsweise der Vergleich bestimmter Feldinhalte. Für eine solche Klasse würde gelten, dass zwei Objektvariablen dieses Typs per Definition dann »gleich« sind, wenn sie sich in den Feldinhalten nicht unterscheiden.

Es könnte beispielsweise festgelegt werden, dass zwei Variablen mit gleichem Feldinhalt grundsätzlich immer dasselbe Objekt referenzieren sollen. Diese Technik, die der Schonung der Speicherressourcen dient, ist nicht neu. String-Objekte werden nach ähnlichen Kriterien erzeugt:


Dim str1 As String = "Hallo Welt"
Dim str2 As String = "Hallo Welt"
Console.WriteLine(str1.GetHashCode())
Console.WriteLine(str1.GetHashCode())

Beide Zeichenfolgen haben denselben Inhalt, referenzieren dieselbe Adresse im Hauptspeicher und zeigen demnach auf dasselbe konkrete Objekt.

Wir wollen nun die Aussage von Equals so definieren, dass zwei Objekte vom Typ ClassA dann als gleich angesehen werden, wenn ihre Eigenschaft MyProp jeweils denselben Inhalt aufweist. Mit dieser Vorgabe wollen wir die Klasse ClassA unseres Beispiels durch die überschriebene Methode Equals ergänzen:


Class ClassA
Public MyProp As Integer
Public Sub New(ByVal i As Integer)
MyProp = i
End Sub
Public Overrides Function Equals(ByVal obj As Object) As Boolean
' Prüfen, ob ein gültiges Objekt übergeben wurde und ob
' dieses auch dem Typ der Klasse entspricht
If ((obj Is Nothing) Or (Not TypeOf obj Is ClassA)) Then
Return False
End If
' Rückgabewert des Feldvergleichs
Return MyProp = obj.MyProp
End Function
End Class

Als Erstes wird in der Methode geprüft, ob ein gültiges Objekt über dem Parameter obj an die Methode übergeben wird und ob dieses auch dem Typ ClassA entspricht. Der letzte Vergleich ist notwendig, da der Parameter vom Typ Object ist und damit die Übergabe jedes x-beliebigen Typs erlaubt.

Sind beide Bedingungen erfüllt, kommt es mit


Return MyProp = obj.MyProp

zu einem Vergleich der beiden Eigenschaften. Sind deren Inhalte identisch, liefert die Vergleichsoperation True zurück, weichen die Inhalte voneinander ab, ist der Rückgabewert False.

Um die Änderung zu testen, müssen wir nur noch ein wenig am Code in Main ändern. Wir wollen jetzt nicht mehr den Nachweis führen, dass ein konkretes Objekt, das über zwei Referenzen angesprochen werden kann, denselben Hashcode aufweist, sondern das Ziel soll lauten, den Nachweis zu führen, dass mit der Überschreibung der Methode Equals auch die Methode GetHashCode überschrieben werden soll.


Module Module1
Sub Main()
Dim obj1 As ClassA = New ClassA(5)
Dim obj2 As ClassA = New ClassA(5)
' Vergleich auf Basis des Hashcodes
If (obj2.GetHashCode() = obj1.GetHashCode()) Then
Console.WriteLine("Hashcode: Objekte sind gleich")
Else
Console.WriteLine("Hashcode: Objekte sind nicht gleich")
End If
' Vergleich auf Basis der Equals-Methode
If (obj2.Equals(obj1)) Then
Console.WriteLine("Equals: Objekte sind gleich")
Else
Console.WriteLine("Equals: Objekte sind nicht gleich")
End If
Console.ReadLine()
End Sub
End Module

Der Vergleich mit der Equals-Methode liefert die Aussage, dass beide Objekte gleich sind, aber der Vergleich mit dem Hashcode schlägt natürlich fehl, weil beide Objekte mit unterschiedlichen Daten aufwarten.

Und genau in diesem Punkt liegt das Dilemma, wenn Sie zwar Equals, aber nicht GetHashCode überschreiben. Ein Benutzer Ihrer Klasse, der Objekte des Typs ClassA in einer Hashtabelle sammelt, wird vielleicht einen Vergleich anhand des Hashcodes ziehen und nicht mit Equals. Es kommt zu einer widersprüchlichen Aussage, die Klasse ist inkonsistent.

Es gehört nicht zu den trivialen Aufgaben, einen passenden Algorithmus zur Generierung des Hashcodes zu implementieren, wenn vom Standard abweichende Vergleichskriterien eine Rolle spielen. Es gibt zu diesem Thema einige gute, wenn auch langatmige Abhandlungen. Es wird jedoch immer ein Wert des Objekts zur Generierung des Hashcodes herangezogen – im einfachsten Fall der Inhalt einer Instanzvariablen des Objekts, z.  B.:


Public Class ClassA
Dim intVar As Integer
' weiterer Klassencode
Public Overrides Function GetHashCode() As Integer
' mathematische Operation mit intVar
' return Hashcode;
End Function
End Class

Das komplette Beispiel finden Sie auf der Buch-CD unter:

...\Kapitel 10\HashCodeOverriding

»ToString« und »GetType«

Wenden wir uns den beiden nächsten Methoden zu: ToString und GetType. Widmen wir uns zunächst der erstgenannten, die wir in den vergangenen Kapiteln schon oft benutzt haben:


Public Overridable Function ToString() As String

ToString liefert per Definition eine Zeichenfolge zurück, die den voll qualifizierenden Namen der Klasse, also einschließlich der Angabe des Namespace, enthält. Viele Klassen überschreiben diese Methode, die dann einen anderen Rückgabewert hat. Sehen wir uns das an zwei Beispielen an:


Dim strText As String = "Visual Basic 2005 ist spitze!"
Console.WriteLine(strText.ToString())
Dim intVar As Integer = 4711
Console.WriteLine(intVar.ToString())

Die Ausgabe lautet:


Visual Basic 2005 ist spitze!

und


4711

Die Typen String und Integer überschreiben demnach ToString und liefern den Inhalt der Variablen, auf welche die Methode aufgerufen worden ist.

Mit GetType können Sie sich den Typ der Klasse besorgen, z.  B.:


Dim intVar As Integer = 10
Console.WriteLine(intVar.GetType())

Jetzt wird nicht der Inhalt der Variablen, sondern der Datentyp ausgegeben. Der Typ des Rückgabewertes ist Type:


Public Function GetType() As Type

Type liefert eine Referenz auf das Type-Objekt eines konkreten Objekts zurück. Dieses versetzt uns in die Lage, den Datentyp einer genaueren Analyse zu unterziehen.

Die Methode »MemberwiseClone«

Die Methode MemberwiseClone kopiert ein vorhandenes Objekt und liefert die Referenz auf die Kopie.


Protected Function MemberwiseClone() As Object

Dabei werden alle Felder des Originals dupliziert: Felder, die auf Wertetypen basieren, werden bitweise kopiert, ebenso die auf Referenztypen basierenden Felder. Das bedeutet, dass die in einem Objekt referenzierten Subobjekte ihrerseits nicht dupliziert werden. Sowohl das Originalobjekt als auch die Kopie greifen auf dieselben Subobjekte zu.

MemberwiseClone ist geschützt deklariert und kann nur aus der aktuellen Instanz heraus aufgerufen werden. Sie müssen daher eine öffentliche Methode bereitstellen, auf die externer Code zugreifen kann. Dazu bietet die .NET-Klassenbibliothek mit ICloneable ein Interface an, dessen Sie sich bedienen sollten. Dieses Interface veröffentlicht nur die Methode Clone:


Function Clone() As Object

Innerhalb einer zu duplizierenden Klasse wird Clone überschrieben. Clone ruft MemberwiseClone auf das aktuelle Objekt auf und liefert als Rückgabe die Referenz auf das Duplikat:


Public Function Clone() As Object
Return Me.MemberwiseClone()
End Function

Etwas komplizierter wird es, wenn in einer Klasse Felder definiert sind, die auf Referenztypen basieren, und Sie gleichzeitig auch alle Subobjekte duplizieren möchten. Schauen Sie sich dazu die Implementierung der Klasse CloneableClass an:


Public Class CloneableClass
Implements ICloneable
Public MyProp As Long
Public internClass As New ClassA
Public Function Clone() As Object _
Implements ICloneable.Clone
Return Me.MemberwiseClone()
End Function
End Class

Die Methode Clone wird an einen Aufrufer die Referenz auf eine Kopie zurückliefern. In der Kopie wird das Feld MyProp denselben Inhalt aufweisen wie das des Originals. Da MemberwiseClone alle Felder bitweise kopiert, referenziert die Eigenschaft internClass des Duplikats jedoch dasselbe Objekt wie das Original. Benötigen wir auch vom internen Objekt eine Kopie, haben wir zwei Möglichkeiten:

gp  Wir erzeugen ein neues Objekt vom Typ ClassA und weisen diesem alle Eigenschaftswerte der Instanz internClass explizit zu.
gp  Wir hoffen, dass der Entwickler der Klasse ClassA weitsichtig genug war und die Klasse ClassA die Schnittstelle ICloneable-Methode implementiert.

Wir wollen an einem Beispiel die unter Punkt 2 genannte Variante studieren. Das Objekt dupliziert sich selbst mit MemberwiseClone und liefert die Referenz der Kopie an den Aufrufer zurück. Exemplarisch nehmen wir die folgende Implementierung an:


Public Class ClassA
Implements ICloneable
Public intVar As Integer
Public Sub New()
Dim rand As New Random
intVar = rand.Next(0, 1000)
End Sub
Public Function Clone() As Object _
Implements ICloneable.Clone
Return Me.MemberwiseClone()
End Function
End Class

Beim Aufruf des parameterlosen Konstruktors wird nach dem Zufallsprinzip eine Zahl zwischen 0 und kleiner 1000 in der Instanzvariablen intVar festgehalten. Sie dient dazu, später den Erfolg des Klonens zu beweisen.

Nun können wir uns noch einmal der Klasse CloneableClass zuwenden und die Methode Clone gemäß unseren Anforderungen überarbeiten.


Public Class CloneableClass
Implements ICloneable
Public MyProp As Long
Public internClass As New ClassA
Public Function Clone() As Object _
Implements ICloneable.Clone
Dim internObj As CloneableClass
internObj = Me.MemberwiseClone()
internObj.internClass = Me.internClass.Clone()
Return internObj
End Function
End Class

Zuerst wird in der Methode Clone die Variable internObj vom Typ CloneableClass deklariert und dieser über MemberwiseClone die Referenz auf das Duplikat zugewiesen:


internObj = Me.MemberwiseClone()

Die Kopie referenziert noch dasselbe interne Objekt wie das Original. Vom Objekt internClass besorgen wir uns deshalb eine Kopie und weisen diese dem Feld internClass der Klone zu.

Damit haben wir unser Ziel erreicht. Was uns jetzt noch fehlt, ist die Bestätigung unserer Überlegungen. Dazu benutzen wir die Methode GetHashCode, da sich jedes Objekt durch einen eigenen Hashcode identifizieren lässt.


' ----------------------------------------------------------
' Beispiel: ...\Kapitel 10\MemberwiseClonen
' ----------------------------------------------------------
Module Module1
Sub Main()
Dim obj As New CloneableClass
Dim dupli As CloneableClass = obj.Clone()
Console.WriteLine("Hashcode(obj) = {0}", _
obj.GetHashCode())
Console.WriteLine("Hashcode(dupli) = {0}", _
dupli.GetHashCode())
Console.WriteLine("Hashcode(obj.internClass) = {0}", _
obj.internClass.GetHashCode())
Console.WriteLine("Hashcode(dupli.internClass) = {0}", _
dupli.internClass.GetHashCode())
Console.WriteLine("intVar(obj) = {0}", _
obj.internClass.intVar)
Console.WriteLine("intVar (dupli) = {0}", _
dupli.internClass.intVar)
Console.ReadLine()
End Sub
End Module

Der Testcode könnte an der Konsole zur folgenden Anzeige führen:


Hashcode(obj) = 70
Hashcode(dupli) = 72
Hashcode(obj.internClass) = 73
Hashcode(dupli.internClass) = 74
internVar(obj) = 768
internVar(dupli) = 768

Die Hashcodes der Referenzen dupli und obj sind unterschiedlich, ebenso die des enthaltenen Feldes vom Typ ClassA. Daraus kann der Schluss gezogen werden, dass das ClassA-Feld des Originals ebenfalls geklont worden ist.

 <<   zurück
  
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Visual Basic 2012






 Visual Basic 2012


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Rheinwerk Verlag GmbH 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de