Objektorientierte Programmierung in Visual Basic .Net

    • VB.NET

    Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

      Objektorientierte Programmierung in Visual Basic .Net

      Hi
      In Vb.Net gibt es Bestandteile aus denen verschiedene Typen aufgebaut werden können. Diese bezeichnet man als Member. Typen können teilweise wiederum Typen enthalten. Zusätzlich lassen sich sogenannte Custom-Member erzeugen, die aber meines Wissens nur in der Reflektion eingelesen bzw. ausgelesen werden können und generell nicht vom Editor unterstützt werden. Die folgende Liste zeigt die Basis aller Member und Typen, auf die die Anderen zurückgehen:
      - Methods (= Methode, Function bzw. Sub)
      - Fields (= Feld, Variablen, Konstanten)
      - Types (= Typ)

      Zusätzlich gibt es noch den Namensraum, in dem sich die Typen zusammenfassen lassen. Ein Typ kann eines der folgenden Modelle darstellen:

      - Classes (=Klasse, enthält Methoden, Events, Properties, Felder, Konstruktoren, etc.)

      VB.NET-Quellcode

      1. Public Class MyClass
      2. Public Function MyMethodInAClass() As Integer
      3. Return -1
      4. End Function
      5. End Class


      - Value types (=Wertetyp, wird in VB.Net als Structure bezeichnet, erbt von System.ValueType, muss mindestens eine Variable enthalten und ist normalerweise nur einmal mit den gleichen Daten im Memory vertreten)

      VB.NET-Quellcode

      1. Public Structure MyStructure
      2. Public intMyVariable As Integer
      3. Public Function MyMethodInAStructure() As Integer
      4. Return intMyVariable \ 2
      5. End Function
      6. End Structure


      - Enumerations (=Auflistung, Auflistung von Konstanten):

      VB.NET-Quellcode

      1. Public Enum MyEnum
      2. [Default] = 0
      3. FirstElement
      4. End Enum


      - Interfaces (=Schnittstelle, Polymorphie bereitstellende Klassen mit abstrakten Methoden und Eigenschaften)

      VB.NET-Quellcode

      1. Public Interface IMyInterface
      2. Function MyInterfaceMethod(ByVal myFirstParameter As Object) As Integer
      3. ReadOnly Property MyInterfaceProperty() As Integer
      4. End Interface


      - Nested types (=verschachtelter Typ, das gleiche wie Klassen, nur dass sie innerhalb von Klassen vertreten sind)

      VB.NET-Quellcode

      1. Public Class X
      2. Private Class MyNestedType
      3. Public Function MyNestedTypeMethod() As Integer
      4. Return -1
      5. End Function
      6. End Class
      7. End Class


      - Delegaten (="Abgesandter", von der [Delegate]-Klasse abgeleitete Klassen, die einem Zeiger auf eine Methode ähnlich sind)

      VB.NET-Quellcode

      1. Public Delegate Function X(ByVal test As Object) As Integer


      - in VB.Net Module (=Modul, Klassen mit statischen Membern)

      VB.NET-Quellcode

      1. Public Module MyModule
      2. Public Function MyModuleMethod() As Integer
      3. Return -1
      4. End Function
      5. End Module


      Und es gibt folgende Member:

      - Events (=Ereignis, Aufbau aus standardmäßig drei Methoden (RaiseEvent, AddHandler, RemoveHandler) und einer Variable, die eine Auflistung von Delegaten enthält)

      VB.NET-Quellcode

      1. Public Event MyEvent As EventHandler

      oder

      VB.NET-Quellcode

      1. Public Custom Event MyCustomEvent As EventHandler
      2. AddHandler(ByVal value As EventHandler)
      3. 'EventHandler hinzufügen
      4. End AddHandler
      5. RemoveHandler(ByVal value As EventHandler)
      6. 'Eventhandler entfernen
      7. End RemoveHandler
      8. RaiseEvent(ByVal sender As Object, ByVal e As System.EventArgs)
      9. 'Event auslösen
      10. End RaiseEvent
      11. End Event


      - Properties (=Eigenschaft, Aufbau aus standardmäßig einer oder zwei Methoden (Set, Get), unterstützt Parameter)

      VB.NET-Quellcode

      1. Public Property MyProperty(ByVal myFirstIndex As Integer,ByVal mySecondIndex As Object) As Object
      2. Get
      3. 'Aufrufe von Methoden etc.
      4. Return 'Rückgabewert
      5. End Get
      6. Set(ByVal value As Object)
      7. 'Wert setzen
      8. End Set
      9. End Property


      - Constructors (=Konstruktor, erste aufgerufene Methode einer Klasse)

      VB.NET-Quellcode

      1. Public Sub New(ByVal myFirstParameter As Object, ByVal mySecondParameter As Object)
      2. End Sub


      - Variables (=Variable, Feld, kann mit Zugriffsmodifizerer versehen werden)

      VB.NET-Quellcode

      1. Public MyVariable As Integer


      - Constants (=Konstante, Feld, kann mit Zugriffsmodifizerer versehen werden, immer statisch)

      VB.NET-Quellcode

      1. Public Const MyConstant As Integer


      Anmerkungen
      Zusätzlich können Methoden, Eigenschaften, Events und Variablen als Statisch deklariert werden (Shared-Modifizierer hinzufügen). Es gibt auch einen statischen Konstruktor, dem kein Parameter übergeben wird. Er dient zur Initialisierung der statischen Komponente einer Klasse oder Struktur. Als Modifizierer versteht man einen Zusatz, der vor einem Member angegeben wird. Mit ihm wird der Zugriff auf den Member und Typen reguliert (z.B. Zugriffsmodifizierer: Private = nur innerhalb des deklarierenden Typs kann auf den Member zugegriffen werden, Public = von überall ist der Zugriff möglich, Friend = nur innerhalb der Assembly kann auf den Member zugegriffen werden, Protected = innerhalb der Erbfolge kann auf diesen Member zugegriffen werden, Protected Friend= Innerhalb der Erbfolge und innerhalb des gleichen Assemblys).

      Wertetypen, die in VB als Strukturen bezeichnet werden, müssen immer mindestens ein Feld besitzen, sonst kann nicht zwischen ihnen unterschieden werden, was spätestens von der Syntaxüberprüfung des Kompilers sowieso verhindert wird. Der Unterschied zwischen Function und Sub ist eigentlich nur der Rückgabetyp. Während Functions diesen besitzen müssen, ist er bei Subs weggelassen (eigentlich ist der Rückgabewert vom Typ System.Void, was auch bei GetType bzw. TypeOf zur Unterscheidung führt. Häufig heißen Subs in anderen Sprachen Voids).

      In VB.Net wird die Erbfolge unterstützt. Sofern die Klasse nicht versiegelt wird (NotInheritable bei den Modifizierern), können also Klassen von diesem Typ abgeleitet werden. Klassen können auch als abstrakt gekennzeichnet werden (MustInherit); von dieser Klasse muss also geerbt werden. Generell gilt, dass eine Klasse nur von einer einzigen anderen Klasse erben kann. Das Problem der Mehrfachvererbung wird durch Polymorphie ergänzt, die ein Implementieren von Interfaces ermöglicht (Implements [Interface]). Interfaces sind generell mit einem I vor dem Namen gekennzeichnet (IEnumerable, ICollection). Dabei werden aber nicht komplette Methoden oder Eigenschaften zur Verfügung gestellt, sondern nur deren Deklaration. Somit kann eine Klasse so aussehen:

      VB.NET-Quellcode

      1. Public Class MyClass
      2. Inherits OtherClass
      3. Implements IEnumerable, IMyInterface
      4. End Class

      Standardmäßig sind Klassen vererbbar.

      Für die Vererbung sind aber nicht nur Klassen elementar. Auch Methoden und Eigenschaften und deren Modifizierer spielen eine große Rolle. Innerhalb der Erbfolge werden beispielsweise alle Zugriffsmodifizierer behalten und nur jene, die direkt von Interfaces implementiert werden, können verändert werden. Um eine Methode "überschreibbar" zu machen, verwendet man entweder den Modifizierer "Overridable" (kann überschrieben werden) oder "MustOverride" (muss überschrieben werden), um die Vererbbarkeit einer Methode zu beenden und die Methode somit zu versiegeln, verwendet man den Modifizierer NotOverridable. Um die Methode schließlich in einer abgeleiteten Klasse zu überschreiben, wird der Overrides-Modifizierer angegeben.
      In einer Klasse

      VB.NET-Quellcode

      1. Public Overridable Sub MyMethod()
      2. End Sub

      In der abgeleiteten Klasse

      VB.NET-Quellcode

      1. Public Overrides Sub MyMethod()
      2. End Sub


      Beim Implementieren von Interfaces ist zu beachten, dass der Zusatz Implements IMyInterface.MyMember benötigt wird. Wenn das Interface von einem anderen Interface erbt, müssen auch dessen Member implementiert werden und somit statt IMyInterface der Name des jeweiligen Interfaces.

      VB.NET-Quellcode

      1. Public Interface IMyInterface
      2. Inherits IEnumerable
      3. Sub MyMethod()
      4. End Interface
      5. Public Class X
      6. Implements IMyInterface
      7. Public Sub MyMethod() Implements IMyInterface.MyMethod
      8. End Sub
      9. Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
      10. End Function
      11. End Class


      Eigenschaften und Felder können auch noch mit ReadOnly versehen werden, die ein Schreiben von außerhalb des Konstruktors verhindern (dazu zählen auch Instanzierungen, die innerhalb der Zeile der Deklaration ablaufen, da diese auch im Konstruktor behandelt werden). Somit sind Felder und Eigenschaften mit diesem Modifizierer zwar lesbar, aber nicht beschreibbar. Zusätzlich gibt es für Eigenschaften noch den WriteOnly-Modifizierer, der nur das Setzen der Eigenschaft zulässt.

      Wenn man auf dieses Konzept aufsetzt, kann man eine ganze Hierarchie erstellen. Alle Klassen erben beispielsweise vom Typ System.Object. Mithilfe der Vererbung wird ein mächtiges Werkzeug präsentiert, das Programme erweiterbar macht und ihre Verwendung findet man in vielen Bereichen wieder (PlugIn-Systeme, Spieleprogrammierung, uvm., aber auch bei Attributen, Ereignis-Argumenten oder Controls).

      Wenn ihr Anregungen, Kritik habt oder einen Fehler entdeckt, könnt ihr das gerne posten. Wer Fragen hat, darf diese natürlich auch stellen und eventuell werden diese dann auch im Post ergänzt.

      Gruß
      ~blaze~

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „~blaze~“ () aus folgendem Grund: Edit1: kleine Formulierungsfehler ausgebessert, Zusatz eingefügt Edit2: weiteren Formulierungsfehler ausgebessert

      In VB.net gibt es keine "Zeiger" in diesem Sinne, stattdessen kannst du mit Referenzen arbeiten. Referenzen sind entweder vom Typ Object oder von einer bestimmten Klasse.
      Gruß, Agent Smith 8-)

      activeFlags = (lazy OR weary)

      Lemgo-Verschwörung | Mathematics | VB-Paradise in blau
      Gibt schon Pointer in VB.Net. Die Verwendung ist allerdings beschränkt. Im Namespace System.Runtime.InteropServices finden sich jedenfalls die ganzen Sachen, die man zum kommunizieren mit einer "unsafe" Welt braucht.

      VB.NET-Quellcode

      1. Private Sub foo()
      2. Dim i As IntPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(1024)
      3. Dim b(100 - 1) As Byte
      4. ' fill with dummy data
      5. For j = 0 To 99
      6. b(j) = j
      7. Next
      8. ' reserve unmanaged mem and copy array to
      9. System.Runtime.InteropServices.Marshal.Copy(b, 0, i + 256, 100)
      10. ' clear array data
      11. Array.Clear(b, 0, 100)
      12. ' copy data back
      13. System.Runtime.InteropServices.Marshal.FreeCoTaskMem(i)
      14. ' free mem
      15. System.Runtime.InteropServices.Marshal.Copy(i + 256, b, 0, 100)
      16. ' show content of b(99) - should be '99'
      17. Debug.Print(b(99))
      18. End Sub
      Wenn du auf das Memory von einem Objekt zugreifen möchtest, dann funktioniert das so. Als erstes musst du das Objekt deklarieren. Dem Objekt fügst du das Attribut StructLayout hinzu. Mit Wahl des richtigen Werts für LayoutKind kann man die Offsets für die einzelnen Felder setzen. Hier mal eine Beispielklasse (für folgende Snippets System.Runtime.InteropServices importieren):

      VB.NET-Quellcode

      1. <StructLayout(LayoutKind.Sequential)> _
      2. Public Class TestObject
      3. Public Test As Integer
      4. End Class


      LayoutKind.Sequential sorgt für eine automatische Anordnung der Felder - im Gegensatz zu Auto sorgt dies für eine feste Position im Memory. Mit Pack kann man genaueres einstellen (Zugriff: Pack:=...). Gibt man statt 'LayoutKind.Sequential' 'LayoutKind.Explicit' an, so kann man den Offset des Feldes mit <FieldOffset(offset in bytes)> für jedes Feld selbst bestimmen. Diese Methode ist aber nicht gut, da sie bei Pointern für 64-Bit-Systeme anders ausfällt, als bei 32-Bit-Systemen. Somit werden Datenwerte unterschiedlich angesprochen, denn diese werden per Pointer gespeichert.

      VB.NET-Quellcode

      1. <StructLayout(LayoutKind.Explicit)> _
      2. Public Class TestObject
      3. <FieldOffset(0)> _
      4. Public Test As Integer
      5. <FieldOffset(4)> _
      6. Public Test2 As Integer
      7. End Class


      Den Wert auslesen kann man einfach so:

      VB.NET-Quellcode

      1. Dim s As New TestObject
      2. s.Test = &H29A
      3. MessageBox.Show(Marshal.ReadInt32(s, Marshal.OffsetOf(GetType(TestObject), "Test").ToInt32).ToString, Application.ProductName)


      Marshal.ReadInt32 liest einen Int32-Wert (Integer) aus. Das Offset des Feldes "Test" wird durch Marshal.OffsetOf(enthaltenderTyp, nameDesFeldes) ermittelt.
      Ähnlich funktioniert das Schreiben des Wertes:

      VB.NET-Quellcode

      1. Dim s As New TestObject
      2. s.Test = &O1232
      3. MessageBox.Show(s.Test.ToString)
      4. Marshal.WriteInt32(s, Marshal.OffsetOf(GetType(TestObject), "Test").ToInt32, &O515)
      5. MessageBox.Show(s.Test.ToString)


      Und eine Instanz kann man auch schön erzeugen:

      VB.NET-Quellcode

      1. Dim instance As TestObject
      2. 'Memory für ein neues Objekt bereitstellen
      3. Dim data As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(GetType(TestObject)))
      4. 'Einen Wert in unsere Raw-Daten schreiben
      5. Marshal.WriteInt32(data, 0, CInt(Math.Ceiling(Math.E ^ 6.5)))
      6. 'Instanz erzeugen
      7. instance = DirectCast(Marshal.PtrToStructure(data, GetType(TestObject)), TestObject)
      8. 'Daten, aus denen die Informationen geladen wurden freigeben
      9. Marshal.FreeCoTaskMem(data)
      10. 'Und den neuen Wert ausgeben
      11. MessageBox.Show(instance.Test.ToString, Application.ProductName)


      Das funktioniert aber nur mit Datentypen, die einen Konstruktor besitzen, der keine Parameter hat. Marshal.PtrToStructure ist in diesem Code das Kernstück. Es kann Memory zu Instanzen von Wertetypen bzw. Datentypen, bei denen das StructLayout-Attribute angegeben wurde, umwandeln. Marshal.SizeOf(typ) gibt die Länge des Typs an. Ist für den Typ keine Längenbestimmung wird eine Ausnahme ausgelöst. Die Umkehrung von Marshal.PtrToStructure ist Marshal.StructureToPtr(instanz, zielHandle, True) (Anmerkung: True kann auch durch False ausgetauscht werden, das kann jedoch ein Memoryleak erzeugen). Dieses funktioniert genau umgekehrt.


      Gruß
      ~blaze~