Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Dieses Kapitel behandelt eine Reihe von Konzepten, die erforderlich sind, um die Semantik der Microsoft Visual Basic-Sprache zu verstehen. Viele der Konzepte sollten visual Basic-Programmierern oder C/C++-Programmierern vertraut sein, ihre genauen Definitionen können sich jedoch unterscheiden.
Deklarationen
Ein Visual Basic-Programm besteht aus benannten Entitäten. Diese Entitäten werden durch Deklarationen eingeführt und stellen die "Bedeutung" des Programms dar.
Auf oberster Ebene sind Namespaces Entitäten , die andere Entitäten organisieren, z. B. geschachtelte Namespaces und Typen. Typen sind Entitäten, die Werte beschreiben und ausführbaren Code definieren. Typen können geschachtelte Typen und Typmber enthalten. Typmember sind Konstanten, Variablen, Methoden, Operatoren, Eigenschaften, Ereignisse, Enumerationswerte und Konstruktoren.
Eine Entität, die andere Entitäten enthalten kann, definiert einen Deklarationsraum. Entitäten werden entweder über Deklarationen oder Vererbung in ein Deklarationsraum eingeführt; der enthaltende Deklarationsraum wird als Deklarationskontext der Entitäten bezeichnet. Durch das Deklarieren einer Entität in einem Deklarationsraum wird wiederum ein neuer Deklarationsbereich definiert, der weitere geschachtelte Entitätsdeklarationen enthalten kann; somit bilden die Deklarationen in einem Programm eine Hierarchie von Deklarationsräumen.
Außer im Falle von überladenen Typmitgliedern ist es ungültig, dass Deklarationen identisch benannte Entitäten derselben Art in den gleichen Deklarationskontext einführen. Darüber hinaus darf ein Deklarationsraum niemals unterschiedliche Arten von Entitäten mit demselben Namen enthalten; Beispielsweise kann ein Deklarationsraum niemals eine Variable und eine Methode mit demselben Namen enthalten.
Hinweis: Es kann in anderen Sprachen möglich sein, ein Deklarationsraum zu erstellen, der unterschiedliche Arten von Entitäten mit demselben Namen enthält (z. B. wenn die Sprache groß- und kleinschreibungsempfindlich ist und unterschiedliche Deklarationen basierend auf der Groß-/Kleinschreibung zulässt). In dieser Situation gilt die barrierefreiste Entität als gebunden an diesen Namen; Wenn auf mehrere Entitätstypen am meisten zugegriffen werden kann, ist der Name mehrdeutig.
Public ist barrierefreier als Protected Friend, Protected Friend ist barrierefreier als Protected oder Friend, und Protected oder Friend ist barrierefreier als Private.
Der Deklarationsraum eines Namespaces ist "geöffnet beendet", sodass zwei Namespacedeklarationen mit demselben vollqualifizierten Namen zum gleichen Deklarationsraum beitragen. Im folgenden Beispiel tragen die beiden Namespacedeklarationen zum gleichen Deklarationsraum bei, in diesem Fall zwei Klassen mit den vollqualifizierten Namen Data.Customer und Data.Order:
Namespace Data
Class Customer
End Class
End Namespace
Namespace Data
Class Order
End Class
End Namespace
Da die beiden Deklarationen zum gleichen Deklarationsbereich beitragen, würde ein Kompilierungszeitfehler auftreten, wenn jede eine Deklaration einer Klasse mit demselben Namen enthielt.
Überladung und Signaturen
Die einzige Möglichkeit, identisch benannte Entitäten derselben Art in einem Deklarationsraum zu deklarieren, erfolgt überladen. Es können nur Methoden, Operatoren, Instanzkonstruktoren und Eigenschaften überladen werden.
Überladene Typenmber müssen über eindeutige Signaturen verfügen. Die Signatur eines Typelements besteht aus der Anzahl der Typparameter sowie der Anzahl und den Typen der Memberparameter. Konvertierungsoperatoren enthalten auch den Rückgabetyp des Operators in der Signatur.
Es folgt kein Teil der Signatur eines Mitglieds und kann daher nicht überladen werden:The following are not part of a member's signature, and daher cannot be overloaded on:
Modifizierer zu einem Typmemm (z. B
Shared. oderPrivate).Modifizierer zu einem Parameter (z. B
ByVal. oderByRef).Die Namen der Parameter.
Der Rückgabetyp einer Methode oder eines Operators (mit Ausnahme von Konvertierungsoperatoren) oder der Elementtyp einer Eigenschaft.
Einschränkungen für einen Typparameter.
Das folgende Beispiel zeigt eine Reihe überladener Methodendeklarationen zusammen mit ihren Signaturen. Diese Deklaration wäre ungültig, da mehrere Methodendeklarationen identische Signaturen aufweisen.
Interface ITest
Sub F1() ' Signature is ().
Sub F2(x As Integer) ' Signature is (Integer).
Sub F3(ByRef x As Integer) ' Signature is (Integer).
Sub F4(x As Integer, y As Integer) ' Signature is (Integer, Integer).
Function F5(s As String) As Integer ' Signature is (String).
Function F6(x As Integer) As Integer ' Signature is (Integer).
Sub F7(a() As String) ' Signature is (String()).
Sub F8(ParamArray a() As String) ' Signature is (String()).
Sub F9(Of T)() ' Signature is !1().
Sub F10(Of T, U)(x As T, y As U) ' Signature is !2(!1, !2)
Sub F11(Of U, T)(x As T, y As U) ' Signature is !2(!2, !1)
Sub F12(Of T)(x As T) ' Signature is !1(!1)
Sub F13(Of T As IDisposable)(x As T) ' Signature is !1(!1)
End Interface
Es ist gültig, einen generischen Typ zu definieren, der Elemente mit identischen Signaturen basierend auf den angegebenen Typargumenten enthalten kann. Überladungsauflösungsregeln werden verwendet, um zwischen solchen Überladungen zu versuchen und zu disambiguieren, obwohl es situationen geben kann, in denen es unmöglich ist, mehrdeutig zu sein. Beispiel:
Class C(Of T)
Sub F(x As Integer)
End Sub
Sub F(x As T)
End Sub
Sub G(Of U)(x As T, y As U)
End Sub
Sub G(Of U)(x As U, y As T)
End Sub
End Class
Module Test
Sub Main()
Dim x As New C(Of Integer)
x.F(10) ' Calls C(Of T).F(Integer)
x.G(Of Integer)(10, 10) ' Error: Can't choose between overloads
End Sub
End Module
Geltungsbereich
Der Bereich des Namens einer Entität ist der Satz aller Deklarationszeichen, in denen es möglich ist, ohne Qualifikation auf diesen Namen zu verweisen. Im Allgemeinen ist der Umfang des Namens einer Entität der gesamte Deklarationskontext; Die Deklaration einer Entität kann jedoch geschachtelte Deklarationen von Entitäten mit demselben Namen enthalten. In diesem Fall sind die geschachtelten Entitätsschatten oder ausgeblendet, die äußere Entität und der Zugriff auf die schattierte Entität nur durch Qualifizierung möglich.
Das Schattieren durch Schachtelung erfolgt in Namespaces oder Typen, die in Namespaces geschachtelt sind, in Typen, die in anderen Typen geschachtelt sind, und in den Textkörpern der Typmmber. Das Schattieren durch die Schachtelung von Deklarationen erfolgt immer implizit; Es ist keine explizite Syntax erforderlich.
Im folgenden Beispiel wird die Instanzvariable innerhalb der F Methode von der lokalen Variablen ischattiert, aber innerhalb der G Methode i bezieht sich immer noch auf die Instanzvariable.i
Class Test
Private i As Integer = 0
Sub F()
Dim i As Integer = 1
End Sub
Sub G()
i = 1
End Sub
End Class
Wenn ein Name in einem inneren Bereich einen Namen in einem äußeren Bereich ausblendet, schattiert er alle überladenen Vorkommen dieses Namens. Im folgenden Beispiel ruft der Aufruf den F deklarierten Inner Aufruf F(1) auf, da alle äußeren Vorkommen der inneren F Deklaration ausgeblendet werden. Aus demselben Grund ist der Aufruf F("Hello") fehlerhaft.
Class Outer
Shared Sub F(i As Integer)
End Sub
Shared Sub F(s As String)
End Sub
Class Inner
Shared Sub F(l As Long)
End Sub
Sub G()
F(1) ' Invokes Outer.Inner.F.
F("Hello") ' Error.
End Sub
End Class
End Class
Vererbung
Eine Vererbungsbeziehung ist eine, bei der ein Typ (der abgeleitete Typ) von einem anderen (dem Basistyp ) abgeleitet wird, sodass der Deklarationsbereich des abgeleiteten Typs implizit die nicht Konstruktortypmber und geschachtelten Typen seines Basistyps enthält. Im folgenden Beispiel ist die Klasse A die Basisklasse von Bund B wird von A.
Class A
End Class
Class B
Inherits A
End Class
Da A keine Basisklasse explizit angegeben wird, wird die Basisklasse implizit Objectangegeben.
Im Folgenden sind wichtige Aspekte der Vererbung aufgeführt:
Vererbung ist transitiv. Wenn Typ C vom Typ B abgeleitet ist und Typ B vom Typ A abgeleitet wird, erbt Typ C die typmember, die in Typ B deklariert sind, sowie die Typmember, die in Typ A deklariert sind.
Ein abgeleiteter Typ erweitert den Basistyp, kann aber nicht schmal sein. Ein abgeleiteter Typ kann neue Typmember hinzufügen, und er kann geerbte Typmember schattenn, aber die Definition eines geerbten Typmembers kann nicht entfernt werden.
Da eine Instanz eines Typs alle Typmember des Basistyps enthält, ist eine Konvertierung immer von einem abgeleiteten Typ in den Basistyp vorhanden.
Alle Typen müssen über einen Basistyp verfügen, mit Ausnahme des Typs
Object.ObjectDaher ist der ultimative Basistyp aller Typen, und alle Typen können in sie konvertiert werden.Zirkularität der Ableitung ist nicht zulässig. Das heißt, wenn ein Typ
Bvon einem TypAabgeleitet wird, handelt es sich um einen Fehler für den TypA, der direkt oder indirekt vom TypBabgeleitet werden soll.Ein Typ kann nicht direkt oder indirekt von einem darin geschachtelten Typ abgeleitet werden.
Im folgenden Beispiel wird ein Kompilierungszeitfehler erzeugt, da die Klassen zirkulär voneinander abhängen.
Class A
Inherits B
End Class
Class B
Inherits C
End Class
Class C
Inherits A
End Class
Im folgenden Beispiel wird auch ein Kompilierungszeitfehler erzeugt, da B er indirekt von der geschachtelten Klasse C über die Klasse Aabgeleitet wird.
Class A
Inherits B.C
End Class
Class B
Inherits A
Public Class C
End Class
End Class
Im nächsten Beispiel wird kein Fehler erzeugt, da die Klasse A nicht von der Klasse Babgeleitet wird.
Class A
Class B
Inherits A
End Class
End Class
MustInherit- und NotInheritable-Klassen
Eine MustInherit Klasse ist ein unvollständiger Typ, der nur als Basistyp fungieren kann. Eine MustInherit Klasse kann nicht instanziiert werden, sodass es sich um einen Fehler handelt, um den New Operator auf einer Instanz zu verwenden. Es ist gültig, Variablen von MustInherit Klassen zu deklarieren. Solche Variablen können nur zugewiesen Nothing werden oder ein Wert, der von der MustInherit Klasse abgeleitet ist.
Wenn eine reguläre Klasse von einer MustInherit Klasse abgeleitet wird, muss die reguläre Klasse alle geerbten MustOverride Member überschreiben. Beispiel:
MustInherit Class A
Public MustOverride Sub F()
End Class
MustInherit Class B
Inherits A
Public Sub G()
End Sub
End Class
Class C
Inherits B
Public Overrides Sub F()
End Sub
End Class
Die MustInherit Klasse A führt eine MustOverride Methode Fein. Klasse B führt eine zusätzliche Methode Gein, bietet jedoch keine Implementierung von F. Klasse B muss daher auch deklariert MustInheritwerden. Die Klasse C überschreibt F und stellt eine konkrete Implementierung bereit. Da es keine herausragenden MustOverride Member in der Klasse Cgibt, ist es nicht erforderlich, MustInherit.
Eine NotInheritable Klasse ist eine Klasse, von der eine andere Klasse nicht abgeleitet werden kann.
NotInheritable Klassen werden in erster Linie verwendet, um unbeabsichtigte Ableitungen zu verhindern.
In diesem Beispiel ist die Klasse B fehlerhaft, da sie versucht, von der NotInheritable Klasse Aabzuleiten. Eine Klasse kann nicht sowohl als auch MustInheritNotInheritablenicht markiert werden.
NotInheritable Class A
End Class
Class B
' Error, a class cannot derive from a NotInheritable class.
Inherits A
End Class
Schnittstellen und mehrfache Vererbung
Im Gegensatz zu anderen Typen, die nur von einem einzelnen Basistyp abgeleitet sind, kann eine Schnittstelle von mehreren Basisschnittstellen abgeleitet werden. Aus diesem Grund kann eine Schnittstelle ein identisch benanntes Typmember von verschiedenen Basisschnittstellen erben. In einem solchen Fall steht der multivererbte Name nicht in der abgeleiteten Schnittstelle zur Verfügung, und das Verweisen auf einen dieser Typenmember über die abgeleitete Schnittstelle verursacht unabhängig von Signaturen oder Überladungen einen Kompilierungszeitfehler. Stattdessen müssen konfliktierende Typenmember über einen Basisschnittstellennamen referenziert werden.
Im folgenden Beispiel verursachen die ersten beiden Anweisungen Kompilierungszeitfehler, da das multivererbte Element Count in der Schnittstelle IListCounternicht verfügbar ist:
Interface IList
Property Count() As Integer
End Interface
Interface ICounter
Sub Count(i As Integer)
End Interface
Interface IListCounter
Inherits IList
Inherits ICounter
End Interface
Module Test
Sub F(x As IListCounter)
x.Count(1) ' Error, Count is not available.
x.Count = 1 ' Error, Count is not available.
CType(x, IList).Count = 1 ' Ok, invokes IList.Count.
CType(x, ICounter).Count(1) ' Ok, invokes ICounter.Count.
End Sub
End Module
Wie im Beispiel dargestellt, wird die Mehrdeutigkeit durch Umwandlung x in den entsprechenden Basisschnittstellentyp aufgelöst. Solche Umwandlungen haben keine Laufzeitkosten; sie bestehen lediglich darin, die Instanz zur Kompilierungszeit als weniger abgeleiteten Typ anzuzeigen.
Wenn ein einzelnes Typmember von derselben Basisschnittstelle über mehrere Pfade geerbt wird, wird das Typmember so behandelt, als ob es nur einmal geerbt wurde. Anders ausgedrückt: Die abgeleitete Schnittstelle enthält nur eine Instanz jedes Typmembers, das von einer bestimmten Basisschnittstelle geerbt wurde. Beispiel:
Interface IBase
Sub F(i As Integer)
End Interface
Interface ILeft
Inherits IBase
End Interface
Interface IRight
Inherits IBase
End Interface
Interface IDerived
Inherits ILeft, IRight
End Interface
Class Derived
Implements IDerived
' Only have to implement F once.
Sub F(i As Integer) Implements IDerived.F
End Sub
End Class
Wenn ein Typelementname in einem Pfad durch die Vererbungshierarchie schattiert wird, wird der Name in allen Pfaden schattiert. Im folgenden Beispiel wird das IBase.F Element vom ILeft.F Element schattiert, ist jedoch nicht schattiert in IRight:
Interface IBase
Sub F(i As Integer)
End Interface
Interface ILeft
Inherits IBase
Shadows Sub F(i As Integer)
End Interface
Interface IRight
Inherits IBase
Sub G()
End Interface
Interface IDerived
Inherits ILeft, IRight
End Interface
Class Test
Sub H(d As IDerived)
d.F(1) ' Invokes ILeft.F.
CType(d, IBase).F(1) ' Invokes IBase.F.
CType(d, ILeft).F(1) ' Invokes ILeft.F.
CType(d, IRight).F(1) ' Invokes IBase.F.
End Sub
End Class
Der Aufruf d.F(1) wählt aus ILeft.F, auch wenn IBase.F er nicht in dem Zugriffspfad schattiert wird, der durch IRightführt. Da der Zugriffspfad von zu IBaseILeft Schatten IBase.FreichtIDerived, wird das Element auch im Zugriffspfad von IDerived zu " IBasenachIRight" schattiert.
Shadowing
Ein abgeleiteter Typ schattiert den Namen eines geerbten Typmembers, indem es erneut deklariert wird. Durch das Schattieren eines Namens werden die geerbten Typmember nicht mit diesem Namen entfernt. es macht lediglich alle geerbten Typm mit diesem Namen in der abgeleiteten Klasse nicht verfügbar. Die Schattendeklaration kann eine beliebige Art von Entität sein.
Entitäten, als überladen werden können, können eine von zwei Formen der Schattenung auswählen.
Die Schattierung anhand des Namens wird mithilfe des Shadows Schlüsselworts angegeben. Eine Entität, die nach Namen schattiert, blendet alles anhand dieses Namens in der Basisklasse aus, einschließlich aller Überladungen.
Der Schatten nach Name und Signatur wird mithilfe des Overloads Schlüsselworts angegeben. Eine Entität, die nach Name und Signatur schattiert, blendet alles mit diesem Namen mit derselben Signatur wie die Entität aus. Beispiel:
Class Base
Sub F()
End Sub
Sub F(i As Integer)
End Sub
Sub G()
End Sub
Sub G(i As Integer)
End Sub
End Class
Class Derived
Inherits Base
' Only hides F(Integer).
Overloads Sub F(i As Integer)
End Sub
' Hides G() and G(Integer).
Shadows Sub G(i As Integer)
End Sub
End Class
Module Test
Sub Main()
Dim x As New Derived()
x.F() ' Calls Base.F().
x.G() ' Error: Missing parameter.
End Sub
End Module
Durch das Schattieren einer Methode mit einem ParamArray Argument nach Name und Signatur werden nur die einzelne Signatur, nicht alle möglichen erweiterten Signaturen, ausgeblendet. Dies gilt auch, wenn die Signatur der Schattenmethode mit der nicht erweiterten Signatur der schattierten Methode übereinstimmt. Das folgende Beispiel:
Class Base
Sub F(ParamArray x() As Integer)
Console.WriteLine("Base")
End Sub
End Class
Class Derived
Inherits Base
Overloads Sub F(x() As Integer)
Console.WriteLine("Derived")
End Sub
End Class
Module Test
Sub Main
Dim d As New Derived()
d.F(10)
End Sub
End Module
druckt Base, obwohl Derived.F die gleiche Signatur wie die unexpandierte Form von Base.F.
Umgekehrt, eine Methode mit einem ParamArray Argument nur Schattenmethoden mit derselben Signatur, nicht alle möglichen erweiterten Signaturen. Das folgende Beispiel:
Class Base
Sub F(x As Integer)
Console.WriteLine("Base")
End Sub
End Class
Class Derived
Inherits Base
Overloads Sub F(ParamArray x() As Integer)
Console.WriteLine("Derived")
End Sub
End Class
Module Test
Sub Main()
Dim d As New Derived()
d.F(10)
End Sub
End Module
druckt , obwohl ein erweitertes BaseFormular mit der gleichen Signatur wie Base.F.Derived.F
Eine Schattenmethode oder -eigenschaft, die nicht angibt Shadows oder Overloads davon ausgeht Overloads , ob die Methode oder Eigenschaft deklariert Overridesist, Shadows andernfalls. Wenn ein Element einer Reihe überladener Entitäten das Shadows oder Overloads das Schlüsselwort angibt, müssen sie alle angeben. Die Shadows Schlüsselwörter können Overloads nicht gleichzeitig angegeben werden. Weder ShadowsOverloads noch kann in einem Standardmodul angegeben werden; Elemente in einem Standardmodul erben Objectimplizit Schattenmember.
Es ist gültig, den Namen eines Typmembers abzuschatten, der durch die Schnittstellenvererbung multipliziert wurde (und damit nicht verfügbar ist), wodurch der Name in der abgeleiteten Schnittstelle verfügbar gemacht wird.
Beispiel:
Interface ILeft
Sub F()
End Interface
Interface IRight
Sub F()
End Interface
Interface ILeftRight
Inherits ILeft, IRight
Shadows Sub F()
End Interface
Module Test
Sub G(i As ILeftRight)
i.F() ' Calls ILeftRight.F.
CType(i, ILeft).F() ' Calls ILeft.F.
CType(i, IRight).F() ' Calls IRight.F.
End Sub
End Module
Da Methoden geerbten Methoden schattenn dürfen, ist es möglich, dass eine Klasse mehrere Overridable Methoden mit derselben Signatur enthält. Dies stellt kein Mehrdeutigkeitsproblem dar, da nur die am häufigsten abgeleitete Methode sichtbar ist. Im folgenden Beispiel enthalten die C Klassen zwei DOverridable Methoden mit derselben Signatur:
Class A
Public Overridable Sub F()
Console.WriteLine("A.F")
End Sub
End Class
Class B
Inherits A
Public Overrides Sub F()
Console.WriteLine("B.F")
End Sub
End Class
Class C
Inherits B
Public Shadows Overridable Sub F()
Console.WriteLine("C.F")
End Sub
End Class
Class D
Inherits C
Public Overrides Sub F()
Console.WriteLine("D.F")
End Sub
End Class
Module Test
Sub Main()
Dim d As New D()
Dim a As A = d
Dim b As B = d
Dim c As C = d
a.F()
b.F()
c.F()
d.F()
End Sub
End Module
Es gibt hier zwei Overridable Methoden: eine von Klasse A und die von Klasse Ceingeführte. Die von der Klasse C eingeführte Methode blendet die von der Klasse Ageerbte Methode aus. Daher überschreibt die Overrides Deklaration in der Klasse D die von der Klasse Ceingeführte Methode, und es ist nicht möglich D , die von der Klasse Aeingeführte Methode außer Kraft zu setzen. Das Beispiel ergibt die Ausgabe:
B.F
B.F
D.F
D.F
Es ist möglich, die ausgeblendete Overridable Methode durch Den Zugriff auf eine Instanz der Klasse D über einen weniger abgeleiteten Typ aufzurufen, in dem die Methode nicht ausgeblendet ist.
Es ist nicht gültig, eine MustOverride Methode abzuschatten, da dies in den meisten Fällen die Klasse unbrauchbar machen würde. Beispiel:
MustInherit Class Base
Public MustOverride Sub F()
End Class
MustInherit Class Derived
Inherits Base
Public Shadows Sub F()
End Sub
End Class
Class MoreDerived
Inherits Derived
' Error: MustOverride method Base.F is not overridden.
End Class
In diesem Fall ist die Klasse MoreDerived erforderlich, um die MustOverride Methode Base.Faußer Kraft zu setzen, aber da die Klassenschatten DerivedBase.Fnicht möglich sind. Es gibt keine Möglichkeit, einen gültigen absteigenden Wert von Derived.
Im Gegensatz zum Schattieren eines Namens aus einem äußeren Bereich bewirkt das Schattieren eines barrierefreien Namens aus einem geerbten Bereich, dass eine Warnung gemeldet wird, wie im folgenden Beispiel gezeigt:
Class Base
Public Sub F()
End Sub
Private Sub G()
End Sub
End Class
Class Derived
Inherits Base
Public Sub F() ' Warning: shadowing an inherited name.
End Sub
Public Sub G() ' No warning, Base.G is not accessible here.
End Sub
End Class
Die Deklaration der Methode F in der Klasse Derived bewirkt, dass eine Warnung gemeldet wird. Das Abschatten eines geerbten Namens ist ausdrücklich kein Fehler, da dies eine separate Entwicklung von Basisklassen ausschließen würde. Die oben genannte Situation könnte beispielsweise auftreten, weil eine spätere Version der Klasse Base eine Methode F eingeführt hat, die in einer früheren Version der Klasse nicht vorhanden war. Wenn die obige Situation ein Fehler war, könnte jede Änderung an einer Basisklasse in einer separat versionsierten Klassenbibliothek möglicherweise dazu führen, dass abgeleitete Klassen ungültig werden.
Die Warnung, die durch Das Schattieren eines geerbten Namens verursacht wird, kann durch die Verwendung des Shadows Modifizierers Overloads eliminiert werden:
Class Base
Public Sub F()
End Sub
End Class
Class Derived
Inherits Base
Public Shadows Sub F() 'OK.
End Sub
End Class
Der Shadows Modifizierer gibt die Absicht an, das geerbte Element abzuschatten. Es handelt sich nicht um einen Fehler, den Shadows oder Overloads den Modifizierer anzugeben, wenn kein Typmemembname für Schatten vorhanden ist.
Eine Deklaration eines neuen Members schattiert ein geerbtes Element nur innerhalb des Gültigkeitsbereichs des neuen Elements, wie im folgenden Beispiel gezeigt:
Class Base
Public Shared Sub F()
End Sub
End Class
Class Derived
Inherits Base
Private Shared Shadows Sub F() ' Shadows Base.F in class Derived only.
End Sub
End Class
Class MoreDerived
Inherits Derived
Shared Sub G()
F() ' Invokes Base.F.
End Sub
End Class
Im obigen Beispiel schattiert die Deklaration der Methode F in klassenschatten Derived die Methode F , die von der Klasse Basegeerbt wurde, aber da die neue Methode F in der Klasse Derived Zugriff hat Private , wird der Bereich nicht auf die Klasse MoreDerivederweitert. Daher ist der Aufruf F()MoreDerived.G gültig und wird aufgerufen Base.F. Bei überladenen Typmmbern wird der gesamte Satz überladener Typmber so behandelt, als hätten sie alle den zulässigsten Zugriff für die Zwecke der Schattierung.
Class Base
Public Sub F()
End Sub
End Class
Class Derived
Inherits Base
Private Shadows Sub F()
End Sub
Public Shadows Sub F(i As Integer)
End Sub
End Class
Class MoreDerived
Inherits Derived
Public Sub G()
F() ' Error. No accessible member with this signature.
End Sub
End Class
In diesem Beispiel wird die überladene F(Integer) Deklaration mit Zugriff deklariert, obwohl die In-Deklaration DerivedF() mit PrivatePublic Zugriff deklariert wird. Daher wird der Name FDerived im Sinne der Schattierung so behandelt, als wäre Publices , also beide Methoden schatten F in Base.
Implementierung
Eine Implementierungsbeziehung ist vorhanden, wenn ein Typ deklariert, dass er eine Schnittstelle implementiert, und der Typ implementiert alle Typmember der Schnittstelle. Ein Typ, der eine bestimmte Schnittstelle implementiert, ist in diese Schnittstelle konvertierbar. Schnittstellen können nicht instanziiert werden, aber sie ist gültig, um Variablen von Schnittstellen zu deklarieren; solchen Variablen kann nur ein Wert zugewiesen werden, der eine Klasse ist, die die Schnittstelle implementiert. Beispiel:
Interface ITestable
Function Test(value As Byte) As Boolean
End Interface
Class TestableClass
Implements ITestable
Function Test(value As Byte) As Boolean Implements ITestable.Test
Return value > 128
End Function
End Class
Module Test
Sub F()
Dim x As ITestable = New TestableClass
Dim b As Boolean
b = x.Test(34)
End Sub
End Module
Ein Typ, der eine Schnittstelle mit multivererbten Typmember implementiert, muss diese Methoden dennoch implementieren, obwohl nicht direkt von der abgeleiteten Schnittstelle aus zugegriffen werden kann, die implementiert wird. Beispiel:
Interface ILeft
Sub Test()
End Interface
Interface IRight
Sub Test()
End Interface
Interface ILeftRight
Inherits ILeft, IRight
End Interface
Class LeftRight
Implements ILeftRight
' Has to reference ILeft explicitly.
Sub TestLeft() Implements ILeft.Test
End Sub
' Has to reference IRight explicitly.
Sub TestRight() Implements IRight.Test
End Sub
' Error: Test is not available in ILeftRight.
Sub TestLeftRight() Implements ILeftRight.Test
End Sub
End Class
Selbst MustInherit Klassen müssen Implementierungen aller Member der implementierten Schnittstellen bereitstellen. Sie können jedoch die Implementierung dieser Methoden zurückstellen, indem sie sie deklarieren als MustOverride. Beispiel:
Interface ITest
Sub Test1()
Sub Test2()
End Interface
MustInherit Class TestBase
Implements ITest
' Provides an implementation.
Sub Test1() Implements ITest.Test1
End Sub
' Defers implementation.
MustOverride Sub Test2() Implements ITest.Test2
End Class
Class TestDerived
Inherits TestBase
' Have to implement MustOverride method.
Overrides Sub Test2()
End Sub
End Class
Ein Typ kann eine Schnittstelle, die vom Basistyp implementiert wird, erneut implementieren. Um die Schnittstelle erneut zu implementieren, muss der Typ explizit angeben, dass sie die Schnittstelle implementiert. Ein Typ, der eine Schnittstelle erneut implementiert, kann nur einige, aber nicht alle Member der Schnittstelle erneut implementieren – alle Elemente, die nicht erneut implementiert werden, verwenden weiterhin die Implementierung des Basistyps. Beispiel:
Class TestBase
Implements ITest
Sub Test1() Implements ITest.Test1
Console.WriteLine("TestBase.Test1")
End Sub
Sub Test2() Implements ITest.Test2
Console.WriteLine("TestBase.Test2")
End Sub
End Class
Class TestDerived
Inherits TestBase
Implements ITest ' Required to re-implement
Sub DerivedTest1() Implements ITest.Test1
Console.WriteLine("TestDerived.DerivedTest1")
End Sub
End Class
Module Test
Sub Main()
Dim Test As ITest = New TestDerived()
Test.Test1()
Test.Test2()
End Sub
End Module
In diesem Beispiel wird gedruckt:
TestDerived.DerivedTest1
TestBase.Test2
Wenn ein abgeleiteter Typ eine Schnittstelle implementiert, deren Basisschnittstellen von den Basistypen des abgeleiteten Typs implementiert werden, kann der abgeleitete Typ auswählen, dass nur die Typmember der Schnittstelle implementiert werden, die noch nicht von den Basistypen implementiert werden. Beispiel:
Interface IBase
Sub Base()
End Interface
Interface IDerived
Inherits IBase
Sub Derived()
End Interface
Class Base
Implements IBase
Public Sub Base() Implements IBase.Base
End Sub
End Class
Class Derived
Inherits Base
Implements IDerived
' Required: IDerived.Derived not implemented by Base.
Public Sub Derived() Implements IDerived.Derived
End Sub
End Class
Eine Schnittstellenmethode kann auch mithilfe einer überschreibbaren Methode in einem Basistyp implementiert werden. In diesem Fall kann ein abgeleiteter Typ auch die überschreibbare Methode außer Kraft setzen und die Implementierung der Schnittstelle ändern. Beispiel:
Class Base
Implements ITest
Public Sub Test1() Implements ITest.Test1
Console.WriteLine("TestBase.Test1")
End Sub
Public Overridable Sub Test2() Implements ITest.Test2
Console.WriteLine("TestBase.Test2")
End Sub
End Class
Class Derived
Inherits Base
' Overrides base implementation.
Public Overrides Sub Test2()
Console.WriteLine("TestDerived.Test2")
End Sub
End Class
Implementieren von Methoden
Ein Typ implementiert ein Typmemm einer implementierten Schnittstelle, indem eine Methode mit einer Implements Klausel bereitgestellt wird. Die beiden Typenmember müssen dieselbe Anzahl von Parametern aufweisen, alle Typen und Modifizierer der Parameter müssen übereinstimmen, einschließlich des Standardwerts optionaler Parameter, der Rückgabetyp muss übereinstimmen, und alle Einschränkungen für Methodenparameter müssen übereinstimmen. Beispiel:
Interface ITest
Sub F(ByRef x As Integer)
Sub G(Optional y As Integer = 20)
Sub H(Paramarray z() As Integer)
End Interface
Class Test
Implements ITest
' Error: ByRef/ByVal mismatch.
Sub F(x As Integer) Implements ITest.F
End Sub
' Error: Defaults do not match.
Sub G(Optional y As Integer = 10) Implements ITest.G
End Sub
' Error: Paramarray does not match.
Sub H(z() As Integer) Implements ITest.H
End Sub
End Class
Eine einzelne Methode kann eine beliebige Anzahl von Schnittstellentypmember implementieren, wenn sie alle die oben genannten Kriterien erfüllen. Beispiel:
Interface ITest
Sub F(i As Integer)
Sub G(i As Integer)
End Interface
Class Test
Implements ITest
Sub F(i As Integer) Implements ITest.F, ITest.G
End Sub
End Class
Bei der Implementierung einer Methode in einer generischen Schnittstelle muss die Implementierungsmethode die Typargumente angeben, die den Typparametern der Schnittstelle entsprechen. Beispiel:
Interface I1(Of U, V)
Sub M(x As U, y As List(Of V))
End Interface
Class C1(Of W, X)
Implements I1(Of W, X)
' W corresponds to U and X corresponds to V
Public Sub M(x As W, y As List(Of X)) Implements I1(Of W, X).M
End Sub
End Class
Class C2
Implements I1(Of String, Integer)
' String corresponds to U and Integer corresponds to V
Public Sub M(x As String, y As List(Of Integer)) _
Implements I1(Of String, Integer).M
End Sub
End Class
Beachten Sie, dass es möglich ist, dass eine generische Schnittstelle für einige Typenargumente möglicherweise nicht implementiert werden kann.
Interface I1(Of T, U)
Sub S1(x As T)
Sub S1(y As U)
End Interface
Class C1
' Unable to implement because I1.S1 has two identical signatures
Implements I1(Of Integer, Integer)
End Class
Polymorphismus
Polymorphismus bietet die Möglichkeit, die Implementierung einer Methode oder Eigenschaft zu variieren. Bei Polymorphismus kann die gleiche Methode oder Eigenschaft je nach Laufzeittyp der Instanz, die sie aufruft, unterschiedliche Aktionen ausführen. Methoden oder Eigenschaften, die polymorph sind, werden als überschreibbar bezeichnet. Im Gegensatz dazu ist die Implementierung einer nicht überschreibbaren Methode oder Eigenschaft invariant; Die Implementierung ist identisch, unabhängig davon, ob die Methode oder Eigenschaft für eine Instanz der Klasse aufgerufen wird, in der sie deklariert wird, oder eine Instanz einer abgeleiteten Klasse. Wenn eine nicht überschreibbare Methode oder Eigenschaft aufgerufen wird, ist der Kompilierungszeittyp der Instanz der bestimmende Faktor. Beispiel:
Class Base
Public Overridable Property X() As Integer
Get
End Get
Set
End Set
End Property
End Class
Class Derived
Inherits Base
Public Overrides Property X() As Integer
Get
End Get
Set
End Set
End Property
End Class
Module Test
Sub F()
Dim Z As Base
Z = New Base()
Z.X = 10 ' Calls Base.X
Z = New Derived()
Z.X = 10 ' Calls Derived.X
End Sub
End Module
Eine überschreibbare Methode kann auch sein MustOverride, was bedeutet, dass sie keinen Methodentext bereitstellt und überschrieben werden muss.
MustOverride Methoden sind nur in MustInherit Klassen zulässig.
Im folgenden Beispiel definiert die Klasse Shape den abstrakten Begriff eines geometrischen Formobjekts, das sich selbst zeichnen kann:
MustInherit Public Class Shape
Public MustOverride Sub Paint(g As Graphics, r As Rectangle)
End Class
Public Class Ellipse
Inherits Shape
Public Overrides Sub Paint(g As Graphics, r As Rectangle)
g.drawEllipse(r)
End Sub
End Class
Public Class Box
Inherits Shape
Public Overrides Sub Paint(g As Graphics, r As Rectangle)
g.drawRect(r)
End Sub
End Class
Die Paint Methode liegt MustOverride daran, dass keine sinnvolle Standardimplementierung vorhanden ist. Die Ellipse- und Box-Klassen sind konkrete Shape-Implementierungen. Da diese Klassen nicht MustInheritvorhanden sind, müssen sie die Paint Methode außer Kraft setzen und eine tatsächliche Implementierung bereitstellen.
Es handelt sich um einen Fehler für einen Basiszugriff, um auf eine MustOverride Methode zu verweisen, wie im folgenden Beispiel veranschaulicht wird:
MustInherit Class A
Public MustOverride Sub F()
End Class
Class B
Inherits A
Public Overrides Sub F()
MyBase.F() ' Error, MyBase.F is MustOverride.
End Sub
End Class
Für den MyBase.F() Aufruf wird ein Fehler gemeldet, da er auf eine MustOverride Methode verweist.
Außerkraftsetzungsmethoden
Ein Typ kann eine geerbte überschreibbare Methode außer Kraft setzen , indem eine Methode mit demselben Namen und , der Signatur und dem Markieren der Deklaration mit dem Overrides Modifizierer deklariert wird. Es gibt zusätzliche Anforderungen an überschreibende Methoden, die unten aufgeführt sind. Während eine Overridable Methodendeklaration eine neue Methode einführt, ersetzt eine Overrides Methodendeklaration die geerbte Implementierung der Methode.
Eine überschreibende Methode kann deklariert NotOverridablewerden, wodurch eine weitere Außerkraftsetzung der Methode in abgeleiteten Typen verhindert wird. In Der Tat NotOverridable werden Methoden in allen weiteren abgeleiteten Klassen nicht außer Kraft gesetzt.
Betrachten Sie das folgenden Beispiel:
Class A
Public Overridable Sub F()
Console.WriteLine("A.F")
End Sub
Public Overridable Sub G()
Console.WriteLine("A.G")
End Sub
End Class
Class B
Inherits A
Public Overrides NotOverridable Sub F()
Console.WriteLine("B.F")
End Sub
Public Overrides Sub G()
Console.WriteLine("B.G")
End Sub
End Class
Class C
Inherits B
Public Overrides Sub G()
Console.WriteLine("C.G")
End Sub
End Class
Im Beispiel stellt die Klasse B zwei Overrides Methoden bereit: eine Methode F mit dem NotOverridable Modifizierer und einer Methode G , die nicht verwendet wird. Die Verwendung des NotOverridable Modifizierers verhindert, dass die Klasse C die methode weiter überschreibt F.
Eine überschreibende Methode kann auch deklariert MustOverridewerden, auch wenn die Methode, die sie überschreibt, nicht deklariert MustOverridewird. Dies erfordert, dass die enthaltende Klasse deklariert MustInherit wird und dass alle weiteren abgeleiteten Klassen, die nicht deklariert MustInherit werden, die Methode überschreiben müssen. Beispiel:
Class A
Public Overridable Sub F()
Console.WriteLine("A.F")
End Sub
End Class
MustInherit Class B
Inherits A
Public Overrides MustOverride Sub F()
End Class
Im Beispiel überschreibt A.F die Klasse B eine MustOverride Methode. Dies bedeutet, dass alle von ihnen abgeleiteten Klassen außer FKraft B setzen müssen, es sei denn, sie werden ebenfalls deklariertMustInherit.
Ein Kompilierungszeitfehler tritt auf, es sei denn, alle folgenden Werte gelten für eine überschreibende Methode:
- Der Deklarationskontext enthält eine einzelne geerbte Methode mit derselben Signatur und einem Rückgabetyp (falls vorhanden) wie die überschreibende Methode.
- Die geerbte Methode, die überschrieben wird, kann überschrieben werden. Mit anderen Worten, die geerbte Methode, die überschrieben wird, ist nicht
SharedoderNotOverridable. - Die Barrierefreiheitsdomäne der deklarierten Methode entspricht der Barrierefreiheitsdomäne der geerbten Methode, die überschrieben wird. Es gibt eine Ausnahme: Eine
Protected FriendMethode muss von einerProtectedMethode überschrieben werden, wenn sich die andere Methode in einer anderen Assembly befindet, auf die die überschreibende Methode keinen Zugriff hatFriend. - Die Parameter der Außerkraftsetzungsmethode stimmen mit den Parametern der überschriebenen Methode in Bezug auf die Verwendung der
ByValModifiziererByRefParamArray,undOptionalModifizierer überein, einschließlich der für optionale Parameter bereitgestellten Werte. - Die Typparameter der Überschreibungsmethode stimmen mit den Typparametern der überschriebenen Methode in Bezug auf Typeinschränkungen überein.
Beim Überschreiben einer Methode in einem generischen Basistyp muss die Überschreibungsmethode die Typargumente angeben, die den Basistypparametern entsprechen. Beispiel:
Class Base(Of U, V)
Public Overridable Sub M(x As U, y As List(Of V))
End Sub
End Class
Class Derived(Of W, X)
Inherits Base(Of W, X)
' W corresponds to U and X corresponds to V
Public Overrides Sub M(x As W, y As List(Of X))
End Sub
End Class
Class MoreDerived
Inherits Derived(Of String, Integer)
' String corresponds to U and Integer corresponds to V
Public Overrides Sub M(x As String, y As List(Of Integer))
End Sub
End Class
Beachten Sie, dass eine überschreibbare Methode in einer generischen Klasse für einige Typenargumente möglicherweise nicht überschrieben werden kann. Wenn die Methode deklariert MustOverridewird, bedeutet dies, dass einige Vererbungsketten möglicherweise nicht möglich sind. Beispiel:
MustInherit Class Base(Of T, U)
Public MustOverride Sub S1(x As T)
Public MustOverride Sub S1(y As U)
End Class
Class Derived
Inherits Base(Of Integer, Integer)
' Error: Can't override both S1's at once
Public Overrides Sub S1(x As Integer)
End Sub
End Class
Eine Außerkraftsetzungsdeklaration kann auf die überschriebene Basismethode mithilfe eines Basiszugriffs zugreifen, wie im folgenden Beispiel gezeigt:
Class Base
Private x As Integer
Public Overridable Sub PrintVariables()
Console.WriteLine("x = " & x)
End Sub
End Class
Class Derived
Inherits Base
Private y As Integer
Public Overrides Sub PrintVariables()
MyBase.PrintVariables()
Console.WriteLine("y = " & y)
End Sub
End Class
Im Beispiel ruft der Aufruf in MyBase.PrintVariables() der Klasse Derived die PrintVariables in der Klasse Basedeklarierte Methode auf. Ein Basiszugriff deaktiviert den überschreibbaren Aufrufmechanismus und behandelt die Basismethode einfach als nicht überschreibbare Methode. Wenn der Aufruf Derived geschrieben CType(Me, Base).PrintVariables()wurde, würde sie rekursiv die PrintVariables in Deriveddeklarierte Methode aufrufen, nicht die in Base.
Nur wenn sie einen Overrides Modifizierer enthält, kann eine Methode eine andere Methode außer Kraft setzen. In allen anderen Fällen schattiert eine Methode mit derselben Signatur wie eine geerbte Methode einfach die geerbte Methode, wie im folgenden Beispiel gezeigt:
Class Base
Public Overridable Sub F()
End Sub
End Class
Class Derived
Inherits Base
Public Overridable Sub F() ' Warning, shadowing inherited F().
End Sub
End Class
Im Beispiel enthält die Methode F in der Klasse Derived keinen Modifizierer und überschreibt daher die Methode F nicht in der KlasseBase.Overrides Stattdessen wird die Methode F in klassenschatten Derived , Baseund eine Warnung wird gemeldet, da die Deklaration keinen Modifizierer enthält ShadowsOverloads .
Im folgenden Beispiel schattiert die Methode F in Klassen Derived die überschreibbare Methode F , die von der Klasse Basegeerbt wurde:
Class Base
Public Overridable Sub F()
End Sub
End Class
Class Derived
Inherits Base
Private Shadows Sub F() ' Shadows Base.F within Derived.
End Sub
End Class
Class MoreDerived
Inherits Derived
Public Overrides Sub F() ' Ok, overrides Base.F.
End Sub
End Class
Da die neue Methode F in der Klasse Derived Zugriff hat Private , umfasst der Bereich nur den Klassentext und Derived erweitert nicht auf die Klasse MoreDerived. Die Deklaration der Methode F in der Klasse MoreDerived darf daher die von der Klasse Basegeerbte Methode F überschreiben.
Wenn eine Overridable Methode aufgerufen wird, wird die abgeleitete Implementierung der Instanzmethode basierend auf dem Typ der Instanz aufgerufen, unabhängig davon, ob der Aufruf der Methode in der Basisklasse oder der abgeleiteten Klasse erfolgt. Die am häufigsten abgeleitete Implementierung einer Overridable Methode M in Bezug auf eine Klasse R wird wie folgt bestimmt:
Wenn
Rdie EinführungserklärungOverridableMenthalten ist, ist dies die abgeleitetste Implementierung vonM.Andernfalls, wenn
Reine Außerkraftsetzung vonMenthält, ist dies die abgeleitetste Implementierung vonM.Andernfalls entspricht die am meisten abgeleitete Implementierung
Mder direkten Basisklasse vonR.
Zugänglichkeit
Eine Deklaration gibt die Barrierefreiheit der entität an, die sie deklariert. Die Barrierefreiheit einer Entität ändert nicht den Bereich des Namens einer Entität. Die Barrierefreiheitsdomäne einer Deklaration ist der Satz aller Deklarationsplätze, in denen auf die deklarierte Entität zugegriffen werden kann.
Die fünf Zugriffstypen sind Public: , Protected, Friend, Protected Friendund Private.
Public ist der zulässigste Zugriffstyp, und die vier anderen Typen sind alle Teilmengen von Public. Der am wenigsten zulässige Zugriffstyp ist Private, und die vier anderen Zugriffstypen sind alle Übersätze von Private.
AccessModifier
: 'Public'
| 'Protected'
| 'Friend'
| 'Private'
| 'Protected' 'Friend'
;
Der Zugriffstyp für eine Deklaration wird über einen optionalen Zugriffsmodifizierer angegeben, der eine FriendPrivateProtectedPublicKombination von Protected und und .Friend Wenn kein Zugriffsmodifizierer angegeben ist, hängt der Standardzugriffstyp vom Deklarationskontext ab. die zulässigen Zugriffstypen hängen auch vom Deklarationskontext ab.
Entitäten, die mit dem
PublicModifizierer deklariert sind, habenPublicZugriff. Es gibt keine Einschränkungen für die Verwendung vonPublicEntitäten.Entitäten, die mit dem
ProtectedModifizierer deklariert sind, habenProtectedZugriff.ProtectedDer Zugriff kann nur für Member von Klassen (sowohl reguläre Typenmber als auch geschachtelte Klassen) oder aufOverridableMembern von Standardmodulen und Strukturen (die per Definition geerbtSystem.Objectwerden müssen)System.ValueTypeangegeben werden. Auf einProtectedElement kann auf eine abgeleitete Klasse zugegriffen werden, vorausgesetzt, das Element ist kein Instanzmemm oder der Zugriff erfolgt über eine Instanz der abgeleiteten Klasse.ProtectedDer Zugriff ist keine Obermenge desFriendZugriffs.Entitäten, die mit dem
FriendModifizierer deklariert sind, habenFriendZugriff. Auf eine Entität mitFriendZugriff kann nur innerhalb des Programms zugegriffen werden, das die Entitätsdeklaration oder Assemblys enthält, die zugriff über dasSystem.Runtime.CompilerServices.InternalsVisibleToAttributeAttribut erhaltenFriendhaben.Entitäten, die mit den
Protected FriendModifizierern deklariert sind, haben die Vereinigung undProtectedFriendden Zugriff.Entitäten, die mit dem
PrivateModifizierer deklariert sind, habenPrivateZugriff. Auf einePrivateEntität kann nur innerhalb des Deklarationskontexts zugegriffen werden, einschließlich aller geschachtelten Entitäten.
Die Barrierefreiheit in einer Deklaration hängt nicht von der Barrierefreiheit des Deklarationskontexts ab. Ein mit Zugriff deklarierter Private Typ kann z. B. ein Typmemm mit Public Zugriff enthalten.
Der folgende Code veranschaulicht verschiedene Barrierefreiheitsdomänen:
Public Class A
Public Shared X As Integer
Friend Shared Y As Integer
Private Shared Z As Integer
End Class
Friend Class B
Public Shared X As Integer
Friend Shared Y As Integer
Private Shared Z As Integer
Public Class C
Public Shared X As Integer
Friend Shared Y As Integer
Private Shared Z As Integer
End Class
Private Class D
Public Shared X As Integer
Friend Shared Y As Integer
Private Shared Z As Integer
End Class
End Class
Die Klassen und Member in diesem Beispiel weisen die folgenden Barrierefreiheitsdomänen auf:
Die Barrierefreiheitsdomäne von
AundA.Xist unbegrenzt.Die Barrierefreiheitsdomäne von
A.Y,B,B.X,B.CB.Y,B.C.XundB.C.Yist das enthaltende Programm.Die Barrierefreiheitsdomäne von
A.ZA.Die Barrierefreiheitsdomäne von
B.Z,B.D,B.D.XundB.D.YistB, einschließlichB.CundB.D.Die Barrierefreiheitsdomäne lautet
B.C.ZB.C.Die Barrierefreiheitsdomäne lautet
B.D.ZB.D.
Wie das Beispiel zeigt, ist die Barrierefreiheitsdomäne eines Elements nie größer als die eines enthaltenden Typs. Auch wenn alle X Mitglieder Barrierefreiheit deklariert haben Public , verfügen alle Aber A.X über Barrierefreiheitsdomänen, die durch einen enthaltenden Typ eingeschränkt sind.
Der Zugriff auf Protected Instanzmber muss über eine Instanz des abgeleiteten Typs erfolgen, sodass nicht verknüpfte Typen keinen Zugriff auf die geschützten Member des anderen erhalten können. Beispiel:
Class User
Protected Password As String
End Class
Class Employee
Inherits User
End Class
Class Guest
Inherits User
Public Function GetPassword(u As User) As String
' Error: protected access has to go through derived type.
Return U.Password
End Function
End Class
Im obigen Beispiel hat die Klasse Guest nur Zugriff auf das geschützte Password Feld, wenn sie mit einer Instanz von Guest. Dadurch wird verhindert Guest , dass sie zugriff auf das Password Feld eines Employee Objekts erhält, indem Sie es einfach in Userdas Objekt umwandeln.
Für die Zwecke des Memberzugriffs Protected in generischen Typen enthält der Deklarationskontext Typparameter. Dies bedeutet, dass ein abgeleiteter Typ mit einem Satz von Typargumenten keinen Zugriff auf die Protected Member eines abgeleiteten Typs mit einem anderen Satz von Typargumenten hat. Beispiel:
Class Base(Of T)
Protected x As T
End Class
Class Derived(Of T)
Inherits Base(Of T)
Public Sub F(y As Derived(Of String))
' Error: Derived(Of T) cannot access Derived(Of String)'s
' protected members
y.x = "a"
End Sub
End Class
Hinweis: Die C#-Sprache (und möglicherweise andere Sprachen) ermöglicht es einem generischen Typ, unabhängig davon, welche Typargumente bereitgestellt werden, auf Member zuzugreifen Protected . Dies sollte beim Entwerfen generischer Klassen Protected mit Membern beachtet werden.
Bestandteiltypen
Die Bestandteiltypen einer Deklaration sind die Typen, auf die durch die Deklaration verwiesen wird. Der Typ einer Konstante, der Rückgabetyp einer Methode und die Parametertypen eines Konstruktors sind z. B. alle Komponententypen. Die Barrierefreiheitsdomäne eines Bestandteiltyps einer Deklaration muss mit der oder einer Obermenge der Barrierefreiheitsdomäne der Deklaration selbst übereinstimmen. Beispiel:
Public Class X
Private Class Y
End Class
' Error: Exposing private class Y outside of X.
Public Function Z() As Y
End Function
' Valid: Not exposing outside of X.
Private Function A() As Y
End Function
End Class
Friend Class B
Private Class C
End Class
' Error: Exposing private class Y outside of B.
Public Function D() As C
End Function
End Class
Typ- und Namespacenamen
Viele Sprachkonstrukte erfordern einen Namespace oder Typ, der angegeben werden muss; diese können mithilfe einer qualifizierten Form des Namespaces oder des Namens des Typs angegeben werden. Ein qualifizierter Name besteht aus einer Reihe von Bezeichnern, die durch Punkte getrennt sind; Der Bezeichner auf der rechten Seite eines Punkts wird im Deklarationsbereich aufgelöst, der durch den Bezeichner auf der linken Seite des Punkts angegeben wird.
Der vollqualifizierte Name eines Namespaces oder Typs ist ein qualifizierter Name, der den Namen aller enthaltenden Namespaces und Typen enthält. Mit anderen Worten, der vollqualifizierte Name eines Namespaces oder Typs lautet N.T, wobei T der Name der Entität und N der vollqualifizierte Name seiner enthaltenden Entität ist.
Das folgende Beispiel zeigt mehrere Namespace- und Typdeklarationen zusammen mit den zugehörigen vollqualifizierten Namen in Zeilenkommentaren.
Class A ' A.
End Class
Namespace X ' X.
Class B ' X.B.
Class C ' X.B.C.
End Class
End Class
Namespace Y ' X.Y.
Class D ' X.Y.D.
End Class
End Namespace
End Namespace
Namespace X.Y ' X.Y.
Class E ' X.Y.E.
End Class
End Namespace
Beachten Sie, dass der Namespace X.Y an zwei verschiedenen Stellen im Quellcode deklariert wurde, aber diese beiden Teildeklarationen stellen nur einen einzigen Namespace namens X.Y dar, der sowohl die Klasse D als auch die Klasse E enthält.
In einigen Fällen kann ein qualifizierter Name mit dem Schlüsselwort Globalbeginnen. Das Schlüsselwort stellt den unbenannten äußersten Namespace dar, der in Situationen nützlich ist, in denen eine Deklaration einen umschließenden Namespace schattiert. Das Global Schlüsselwort ermöglicht in dieser Situation das Ausfangen von "escaping" in den äußersten Namespace. Beispiel:
Namespace NS1
Class System
End Class
Module Test
Sub Main()
' Error: Class System does not contain Int32
Dim x As System.Int32
' Legal, binds to System in outermost namespace
Dim y As Global.System.Int32
End Sub
End Module
End Namespace
Im obigen Beispiel ist der erste Methodenaufruf ungültig, da der Bezeichner System an die Klasse Systemund nicht an den Namespace Systemgebunden ist. Die einzige Möglichkeit für den Zugriff auf den System Namespace besteht darin, das Escapezeichen für den äußersten Namespace zu verwenden Global .
Global kann nicht in einer Imports Anweisung oder Namespace Deklaration verwendet werden.
Da in anderen Sprachen Typen und Namespaces eingeführt werden können, die Schlüsselwörter in der Sprache entsprechen, erkennt Visual Basic Schlüsselwörter als Teil eines qualifizierten Namens, solange sie einem Punkt folgen. Schlüsselwörter, die auf diese Weise verwendet werden, werden als Bezeichner behandelt. Der qualifizierte Bezeichner ist z. B. ein gültiger qualifizierter Bezeichner X.Default.Class , aber Default.Class nicht.
Qualifizierte Namensauflösung für Namespaces und Typen
Angesichts eines qualifizierten Namespaces oder Typnamens des Formulars N.R(Of A), wobei R es sich um den am weitesten rechts sten Bezeichner im qualifizierten Namen handelt und A eine optionale Typargumentliste ist, beschreiben die folgenden Schritte, wie sie bestimmen, auf welchen Namespace oder Typ der qualifizierte Name verweist:
Lösen Sie
Ndie Regeln für qualifizierte oder nicht qualifizierte Namensauflösung auf.Wenn die Auflösung eines
NFehlers auftritt oder in einen Typparameter aufgelöst wird, tritt ein Kompilierungszeitfehler auf.Andernfalls, wenn
Rder Name eines Namespaces in N übereinstimmt und keine Typargumente angegeben wurden, oderRentspricht ein barrierefreier TypNmit der gleichen Anzahl von Typparametern wie Typargumente, falls vorhanden, bezieht sich der qualifizierte Name auf diesen Namespace oder Typ.Andernfalls entspricht der
NName eines oder mehrerer Standardmodule demRNamen eines barrierefreien Typs mit der gleichen Anzahl von Typparametern wie Typargumente, falls vorhanden, in genau einem Standardmodul, dann bezieht sich der qualifizierte Name auf diesen Typ. WennRder Name von barrierefreien Typen mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, tritt in mehr als einem Standardmodul ein Kompilierungszeitfehler auf.Andernfalls tritt ein Kompilierfehler auf.
Hinweis: Eine Auswirkung dieses Lösungsprozesses besteht darin, dass Typmmber keine Schattennamespaces oder Typen beim Auflösen von Namespace- oder Typnamen verwenden.
Nicht qualifizierte Namensauflösung für Namespaces und Typen
Bei einem nicht qualifizierten Namen R(Of A), wobei A es sich um eine optionale Typargumentliste handelt, beschreiben die folgenden Schritte, wie Sie bestimmen, auf welchen Namespace oder Typ der nicht qualifizierte Name verweist:
Wenn R mit dem Namen eines Typparameters der aktuellen Methode übereinstimmt und keine Typargumente angegeben wurden, verweist der nicht qualifizierte Name auf diesen Typparameter.
Für jeden geschachtelten Typ, der den Namensverweis enthält, beginnend mit dem innersten Typ und zum äußersten Rand:
- Wenn
Rder Name eines Typparameters im aktuellen Typ übereinstimmt und keine Typargumente angegeben wurden, verweist der nicht qualifizierte Name auf diesen Typparameter. - Andernfalls entspricht der
RName eines geschachtelten Typs mit derselben Anzahl von Typparametern wie Typargumente, falls vorhanden, der nicht qualifizierte Name verweist auf diesen Typ.
- Wenn
Für jeden geschachtelten Namespace, der den Namensverweis enthält, beginnend mit dem innersten Namespace und dem äußersten Namespace:
- Wenn
Rder Name eines geschachtelten Namespaces im aktuellen Namespace entspricht und keine Typargumentliste angegeben wird, bezieht sich der nicht qualifizierte Name auf diesen geschachtelten Namespace. - Andernfalls entspricht der
RName eines barrierefreien Typs mit der gleichen Anzahl von Typparametern wie Typargumente( falls vorhanden) im aktuellen Namespace, dann bezieht sich der nicht qualifizierte Name auf diesen Typ. - Wenn der Namespace ein oder mehrere barrierefreie Standardmodule enthält und
Rdem Namen eines verschachtelten Typs mit derselben Anzahl von Typparametern entspricht, wie Typargumente, falls vorhanden, in genau einem Standardmodul bezieht sich der nicht qualifizierte Name auf diesen geschachtelten Typ. WennRder Name von geschachtelten Typen mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, tritt in mehr als einem Standardmodul ein Kompilierungsfehler auf.
- Wenn
Wenn die Quelldatei einen oder mehrere Importalias enthält und
Rdem Namen eines dieser Aliase entspricht, bezieht sich der nicht qualifizierte Name auf diesen Importalias. Wenn eine Typargumentliste angegeben wird, tritt ein Kompilierungszeitfehler auf.Wenn die Quelldatei, die den Namensverweis enthält, einen oder mehrere Importe hat:
- Wenn
Rder Name eines barrierefreien Typs mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, bezieht sich der nicht qualifizierte Name auf diesen Typ, falls vorhanden, in genau einem Import. WennRder Name eines barrierefreien Typs mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, tritt bei mehreren Importen und alle nicht demselben Typ ein Kompilierungszeitfehler auf. - Andernfalls, wenn keine Typargumentliste angegeben wurde und
Rdem Namen eines Namespaces mit barrierefreien Typen in genau einem Import entspricht, bezieht sich der nicht qualifizierte Name auf diesen Namespace. Wenn keine Typargumentliste angegeben wurde undRdem Namen eines Namespaces mit barrierefreien Typen in mehr als einem Import entspricht und alle nicht derselbe Namespace sind, tritt ein Kompilierungszeitfehler auf. - Andernfalls, wenn die Importe ein oder mehrere barrierefreie Standardmodule enthalten und
Rdem Namen eines verschachtelten Typs mit derselben Anzahl von Typparametern entsprechen wie Typargumente, falls vorhanden, in genau einem Standardmodul, bezieht sich der nicht qualifizierte Name auf diesen Typ. WennRder Name von geschachtelten Typen mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, tritt in mehr als einem Standardmodul ein Kompilierungsfehler auf.
- Wenn
Wenn die Kompilierungsumgebung einen oder mehrere Importalias definiert und
Rdem Namen eines dieser Aliase entspricht, bezieht sich der nicht qualifizierte Name auf diesen Importalias. Wenn eine Typargumentliste angegeben wird, tritt ein Kompilierungszeitfehler auf.Wenn die Kompilierungsumgebung einen oder mehrere Importe definiert:
- Wenn
Rder Name eines barrierefreien Typs mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, bezieht sich der nicht qualifizierte Name auf diesen Typ, falls vorhanden, in genau einem Import. WennRder Name eines barrierefreien Typs mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, tritt bei mehr als einem Import ein Kompilierungszeitfehler auf. - Andernfalls, wenn keine Typargumentliste angegeben wurde und
Rdem Namen eines Namespaces mit barrierefreien Typen in genau einem Import entspricht, bezieht sich der nicht qualifizierte Name auf diesen Namespace. Wenn keine Typargumentliste angegeben wurde undRdem Namen eines Namespaces mit barrierefreien Typen in mehr als einem Import entspricht, tritt ein Kompilierungszeitfehler auf. - Andernfalls, wenn die Importe ein oder mehrere barrierefreie Standardmodule enthalten und
Rdem Namen eines verschachtelten Typs mit derselben Anzahl von Typparametern entsprechen wie Typargumente, falls vorhanden, in genau einem Standardmodul, bezieht sich der nicht qualifizierte Name auf diesen Typ. WennRder Name von geschachtelten Typen mit der gleichen Anzahl von Typparametern wie Typargumente übereinstimmt, tritt in mehr als einem Standardmodul ein Kompilierungsfehler auf.
- Wenn
Andernfalls tritt ein Kompilierfehler auf.
Hinweis: Eine Auswirkung dieses Lösungsprozesses besteht darin, dass Typmmber keine Schattennamespaces oder Typen beim Auflösen von Namespace- oder Typnamen verwenden.
Normalerweise kann ein Name nur einmal in einem bestimmten Namespace vorkommen. Da Namespaces jedoch über mehrere .NET-Assemblys deklariert werden können, ist es möglich, eine Situation zu haben, in der zwei Assemblys einen Typ mit demselben vollqualifizierten Namen definieren. In diesem Fall wird ein im aktuellen Satz von Quelldateien deklarierter Typ gegenüber einem Typ bevorzugt, der in einer externen .NET-Assembly deklariert ist. Andernfalls ist der Name mehrdeutig und es gibt keine Möglichkeit, den Namen zu disambiguieren.
Variablen
Eine Variable stellt einen Speicherort dar. Jede Variable hat einen Typ, der bestimmt, welche Werte in der Variable gespeichert werden können. Da Visual Basic eine typsichere Sprache ist, verfügt jede Variable in einem Programm über einen Typ, und die Sprache garantiert, dass in Variablen gespeicherte Werte immer vom geeigneten Typ sind. Variablen werden immer auf den Standardwert ihres Typs initialisiert, bevor ein Verweis auf die Variable vorgenommen werden kann. Es ist nicht möglich, auf nicht initialisierte Arbeitsspeicher zuzugreifen.
Generische Typen und Methoden
Typen (mit Ausnahme von Standardmodulen und Enumerationstypen) und Methoden können Typparameter deklarieren, bei denen es sich um Typen handelt, die erst bereitgestellt werden, wenn eine Instanz des Typs deklariert oder die Methode aufgerufen wird. Typen und Methoden mit Typparametern werden auch als generische Typen und generische Methoden bezeichnet, da der Typ oder die Methode generisch geschrieben werden muss, ohne spezifische Kenntnisse der Typen, die von Code bereitgestellt werden, der den Typ oder die Methode verwendet.
Hinweis: Zu diesem Zeitpunkt können Methoden und Stellvertretungen generisch, Eigenschaften, Ereignisse und Operatoren selbst nicht generisch sein. Sie können jedoch Typparameter aus der enthaltenden Klasse verwenden.
Aus Sicht des generischen Typs oder der Methode ist ein Typparameter ein Platzhaltertyp, der bei Verwendung des Typs oder der Methode mit einem tatsächlichen Typ gefüllt wird. Typargumente werden durch die Typparameter im Typ oder die Methode an dem Punkt ersetzt, an dem der Typ oder die Methode verwendet wird. Eine generische Stapelklasse könnte beispielsweise wie:
Public Class Stack(Of ItemType)
Protected Items(0 To 99) As ItemType
Protected CurrentIndex As Integer = 0
Public Sub Push(data As ItemType)
If CurrentIndex = 100 Then
Throw New ArgumentException("Stack is full.")
End If
Items(CurrentIndex) = Data
CurrentIndex += 1
End Sub
Public Function Pop() As ItemType
If CurrentIndex = 0 Then
Throw New ArgumentException("Stack is empty.")
End If
CurrentIndex -= 1
Return Items(CurrentIndex + 1)
End Function
End Class
Deklarationen, die die Stack(Of ItemType) Klasse verwenden, müssen ein Typargument für den Typparameter ItemTypeangeben. Dieser Typ wird dann überall ItemType in der Klasse ausgefüllt:
Option Strict On
Module Test
Sub Main()
Dim s1 As New Stack(Of Integer)()
Dim s2 As New Stack(Of Double)()
s1.Push(10.10) ' Error: Stack(Of Integer).Push takes an Integer
s2.Push(10.10) ' OK: Stack(Of Double).Push takes a Double
Console.WriteLine(s2.Pop().GetType().ToString()) ' Prints: Double
End Sub
End Module
Typparameter
Typparameter können für Typ- oder Methodendeklarationen angegeben werden. Jeder Typparameter ist ein Bezeichner, der ein Platzhalter für ein Typargument ist, das zum Erstellen eines konstruierten Typs oder einer konstruierten Methode bereitgestellt wird. Im Gegensatz dazu ist ein Typargument der tatsächliche Typ, der für den Typparameter ersetzt wird, wenn ein generischer Typ oder eine generische Methode verwendet wird.
TypeParameterList
: OpenParenthesis 'Of' TypeParameter ( Comma TypeParameter )* CloseParenthesis
;
TypeParameter
: VarianceModifier? Identifier TypeParameterConstraints?
;
VarianceModifier
: 'In' | 'Out'
;
Jeder Typparameter in einer Typ- oder Methodendeklaration definiert einen Namen im Deklarationsbereich dieses Typs oder dieser Methode. Daher kann er nicht denselben Namen wie ein anderer Typparameter, ein Typmemm, einen Methodenparameter oder eine lokale Variable aufweisen. Der Bereich eines Typparameters für einen Typ oder eine Methode ist der gesamte Typ oder die gesamte Methode. Da Typparameter auf die gesamte Typdeklaration festgelegt sind, können geschachtelte Typen äußere Typparameter verwenden. Dies bedeutet auch, dass Typenparameter immer angegeben werden müssen, wenn auf Typen zugegriffen wird, die in generischen Typen geschachtelt sind:
Public Class Outer(Of T)
Public Class Inner
Public Sub F(x As T)
...
End Sub
End Class
End Class
Module Test
Sub Main()
Dim x As New Outer(Of Integer).Inner()
...
End Sub
End Module
Im Gegensatz zu anderen Membern einer Klasse werden Typparameter nicht geerbt. Typparameter in einem Typ können nur mit ihrem einfachen Namen referenziert werden; Mit anderen Worten, sie können nicht mit dem enthaltenden Typnamen qualifiziert werden. Obwohl es sich um einen schlechten Programmierstil handelt, können die Typparameter in einem geschachtelten Typ einen Element- oder Typparameter ausblenden, der im äußeren Typ deklariert ist:
Class Outer(Of T)
Class Inner(Of T)
Public t1 As T ' Refers to Inner's T
End Class
End Class
Typen und Methoden können basierend auf der Anzahl der Typparameter (oder Desarität) überladen werden, die von den Typen oder Methoden deklariert werden. Die folgenden Erklärungen sind z. B. legal:
Module C
Sub M()
End Sub
Sub M(Of T)()
End Sub
Sub M(Of T, U)()
End Sub
End Module
Structure C(Of T)
Dim x As T
End Structure
Class C(Of T, U)
End Class
Bei Typen werden Überladungen immer mit der Anzahl der angegebenen Typargumente abgeglichen. Dies ist nützlich, wenn sie sowohl generische als auch nicht generische Klassen zusammen im selben Programm verwenden:
Class Queue
End Class
Class Queue(Of T)
End Class
Class X
Dim q1 As Queue ' Non-generic queue
Dim q2 As Queue(Of Integer) ' Generic queue
End Class
Regeln für Methoden, die für Typparameter überladen sind, werden im Abschnitt zur Methodenüberladungsauflösung behandelt.
In der enthaltenden Deklaration werden Typparameter als vollständige Typen betrachtet. Da ein Typparameter mit vielen verschiedenen tatsächlichen Typargumenten instanziiert werden kann, weisen Typparameter geringfügig unterschiedliche Vorgänge und Einschränkungen als andere Typen auf, wie unten beschrieben:
Ein Typparameter kann nicht direkt verwendet werden, um eine Basisklasse oder Schnittstelle zu deklarieren.
Die Regeln für die Elementsuche nach Typparametern hängen ggf. von den Einschränkungen ab, die auf den Typparameter angewendet werden.
Die verfügbaren Konvertierungen für einen Typparameter hängen ggf. von den Einschränkungen ab, die auf die Typparameter angewendet werden.
Wenn keine
StructureEinschränkung vorhanden ist, kann ein Wert mit einem Typ, der durch einen Typparameter dargestellt wird, mitNothing"usingIs" undIsNot".Ein Typparameter kann nur in einem
NewAusdruck verwendet werden, wenn der Typparameter durch eineNewoder eineStructureEinschränkung eingeschränkt wird.Ein Typparameter kann nicht an einer beliebigen Stelle innerhalb einer Attributausnahme innerhalb eines Ausdrucks
GetTypeverwendet werden.Typparameter können als Typargumente für andere generische Typen und Parameter verwendet werden.
Das folgende Beispiel ist ein generischer Typ, der die Stack(Of ItemType) Klasse erweitert:
Class MyStack(Of ItemType)
Inherits Stack(Of ItemType)
Public ReadOnly Property Size() As Integer
Get
Return CurrentIndex
End Get
End Property
End Class
Wenn eine Deklaration ein Typargument angibt MyStack, wird auch das gleiche Typargument angewendet Stack .
Als Typ sind Typparameter rein ein Kompilierungszeitkonstrukt. Zur Laufzeit ist jeder Typparameter an einen Laufzeittyp gebunden, der durch Angeben eines Typarguments an die generische Deklaration angegeben wurde. Daher ist der Typ einer variablen, die mit einem Typparameter deklariert wird, zur Laufzeit ein nicht generischer Typ oder ein bestimmter konstruierter Typ. Die Laufzeitausführung aller Anweisungen und Ausdrücke mit Typparametern verwendet den tatsächlichen Typ, der als Typargument für diesen Parameter angegeben wurde.
Typeinschränkungen
Da ein Typargument ein beliebiger Typ im Typsystem sein kann, kann ein generischer Typ oder eine Methode keine Annahmen zu einem Typparameter machen. Daher werden die Member eines Typparameters als Member des Typs Objectbetrachtet, da alle Typen von Object.
Im Fall einer Sammlung wie Stack(Of ItemType), kann diese Tatsache keine besonders wichtige Einschränkung sein, aber es kann Fälle geben, in denen ein generischer Typ eine Annahme über die Typen vornehmen möchte, die als Typargumente angegeben werden.
Typeinschränkungen können für Typparameter festgelegt werden, die einschränken, welche Typen als Typparameter bereitgestellt werden können, und generische Typen oder Methoden erlauben, mehr über Typparameter zu übernehmen.
TypeParameterConstraints
: 'As' Constraint
| 'As' OpenCurlyBrace ConstraintList CloseCurlyBrace
;
ConstraintList
: Constraint ( Comma Constraint )*
;
Constraint
: TypeName
| 'New'
| 'Structure'
| 'Class'
;
Public Class DisposableStack(Of ItemType As IDisposable)
Implements IDisposable
Private _items(0 To 99) As ItemType
Private _currentIndex As Integer = 0
Public Sub Push(data As ItemType)
...
End Sub
Public Function Pop() As ItemType
...
End Function
Private Sub Dispose() Implements IDisposable.Dispose
For Each item As IDisposable In _items
If item IsNot Nothing Then
item.Dispose()
End If
Next item
End Sub
End Class
In diesem Beispiel schränkt der DisposableStack(Of ItemType) Typparameter nur typen ein, die die Schnittstelle System.IDisposableimplementieren. Daher kann sie eine Dispose Methode implementieren, die alle Objekte entfernt, die noch in der Warteschlange verbleiben.
Eine Typeinschränkung muss eine der besonderen Einschränkungen Classsein, Structureoder Newes muss ein Typ sein, in T dem:
Tist eine Klasse, eine Schnittstelle oder ein Typparameter.Tist nichtNotInheritable.Tist kein Typ oder ein Typ, der von einem der folgenden speziellen Typen geerbt wird:System.Array, , ,System.Delegate,System.MulticastDelegate, ,System.EnumoderSystem.ValueType.Tist nichtObject. Da alle Typen vonObjectdieser Einschränkung abgeleitet sind, hätte eine solche Einschränkung keine Auswirkung, wenn sie zulässig wäre.Tmuss mindestens so barrierefrei sein wie der generische Typ oder die deklarierte Methode.
Mehrere Typeinschränkungen können für einen einzelnen Typparameter angegeben werden, indem die Typeinschränkungen in geschweifte Klammern ({}).) eingeschlossen werden. Nur eine Typeinschränkung für einen bestimmten Typparameter kann eine Klasse sein. Es ist ein Fehler, eine Structure spezielle Einschränkung mit einer benannten Klasseneinschränkung oder der Class speziellen Einschränkung zu kombinieren.
Class ControlFactory(Of T As {Control, New})
...
End Class
Typeinschränkungen können die enthaltenden Typen oder einen der Typenparameter verwenden. Im folgenden Beispiel erfordert die Einschränkung, dass das angegebene Typargument eine generische Schnittstelle als Typargument implementiert:
Class Sorter(Of V As IComparable(Of V))
...
End Class
Die spezielle Typeinschränkung Class schränkt das angegebene Typargument auf einen beliebigen Bezugstyp ein.
Hinweis: Die spezielle Typeinschränkung Class kann von einer Schnittstelle erfüllt werden. Und eine Struktur kann eine Schnittstelle implementieren. Daher kann die Einschränkung (Of T As U, U As Class) mit "T" einer Struktur (die die Class besondere Einschränkung nicht erfüllt) zufrieden sein, und "U" eine Schnittstelle, die sie implementiert (was die Class spezielle Einschränkung erfüllt).
Die spezielle Typeinschränkung Structure schränkt das angegebene Typargument auf einen beliebigen Werttyp außer System.Nullable(Of T)ein.
Hinweis: Struktureinschränkungen lassen nicht zu System.Nullable(Of T) , sodass es nicht möglich ist, als Typargument an sich selbst zu liefern System.Nullable(Of T) .
Die spezielle Typeinschränkung New erfordert, dass das angegebene Typargument über einen barrierefreien parameterlosen Konstruktor verfügen muss und nicht deklariert MustInheritwerden kann. Beispiel:
Class Factory(Of T As New)
Function CreateInstance() As T
Return New T()
End Function
End Class
Eine Klassentypeinschränkung erfordert, dass das angegebene Typargument entweder diesen Typ als oder erben muss. Eine Schnittstellentypeinschränkung erfordert, dass das angegebene Typargument diese Schnittstelle implementieren muss. Eine Typparametereinschränkung erfordert, dass das angegebene Typargument von allen Für den übereinstimmenden Typparameter angegebenen Grenzen abgeleitet oder implementiert werden muss. Beispiel:
Class List(Of T)
Sub AddRange(Of S As T)(collection As IEnumerable(Of S))
...
End Sub
End Class
In diesem Beispiel ist der Typparameter S auf AddRange den Typparameter T von List. Dies bedeutet, dass ein List(Of Control) Typparameter auf einen beliebigen Typ beschränkt AddRangewird, der von diesen erbt oder von Control.
Eine Typparametereinschränkung Of S As T wird durch transitives Hinzufügen aller T'Einschränkungen auf S' außer den speziellen Einschränkungen (Class, , StructureNew) aufgelöst. Es ist ein Fehler, Zirkeleinschränkungen (z. B. Of S As T, T As S) zu haben. Es ist ein Fehler, eine Typparametereinschränkung zu haben, die selbst über die Structure Einschränkung verfügt. Nach dem Hinzufügen von Einschränkungen ist es möglich, dass eine Reihe von besonderen Situationen auftreten kann:
Wenn mehrere Klasseneinschränkungen vorhanden sind, wird die am häufigsten abgeleitete Klasse als Einschränkung betrachtet. Wenn eine oder mehrere Klasseneinschränkungen keine Vererbungsbeziehung aufweisen, ist die Einschränkung nicht zufriedenstellend und ein Fehler.
Wenn ein Typparameter eine
Structurespezielle Einschränkung mit einer benannten Klasseneinschränkung oder derClassspeziellen Einschränkung kombiniert, handelt es sich um einen Fehler. Eine Klasseneinschränkung kann seinNotInheritable, in diesem Fall werden keine abgeleiteten Typen dieser Einschränkung akzeptiert, und es handelt sich um einen Fehler.
Der Typ kann einer von oder einem Typ sein, der von den folgenden speziellen Typen geerbt wird: System.Array, , , System.Delegate, System.MulticastDelegate, , System.Enumoder System.ValueType. In diesem Fall wird nur der Typ oder ein von ihr geerbter Typ akzeptiert. Ein Typparameter, der DirectCast auf einen dieser Typen beschränkt ist, kann nur die vom Operator zulässigen Konvertierungen verwenden. Beispiel:
MustInherit Class Base(Of T)
MustOverride Sub S1(Of U As T)(x As U)
End Class
Class Derived
Inherits Base(Of Integer)
' The constraint of U must be Integer, which is normally not allowed.
Overrides Sub S1(Of U As Integer)(x As U)
Dim y As Integer = x ' OK
Dim z As Long = x ' Error: Can't convert
End Sub
End Class
Darüber hinaus kann ein Typparameter, der aufgrund einer der oben genannten Entspannungen auf einen Werttyp beschränkt ist, keine Methoden aufrufen, die für diesen Werttyp definiert sind. Beispiel:
Class C1(Of T)
Overridable Sub F(Of G As T)(x As G)
End Sub
End Class
Class C2
Inherits C1(Of IntPtr)
Overrides Sub F(Of G As IntPtr)(ByVal x As G)
' Error: Cannot access structure members
x.ToInt32()
End Sub
End Class
Wenn die Einschränkung nach der Ersetzung als Arraytyp endet, ist auch jeder kovariante Arraytyp zulässig. Beispiel:
Module Test
Class B
End Class
Class D
Inherits B
End Class
Function F(Of T, U As T)(x As U) As T
Return x
End Function
Sub Main()
Dim a(9) As B
Dim b(9) As D
a = F(Of B(), D())(b)
End Sub
End Module
Ein Typparameter mit einer Klassen- oder Schnittstelleneinschränkung gilt als die gleichen Member wie diese Klasse oder Schnittstelleneinschränkung. Wenn ein Typparameter mehrere Einschränkungen aufweist, wird der Typparameter als Vereinigung aller Member der Einschränkungen betrachtet. Wenn Mitglieder mit demselben Namen in mehr als einer Einschränkung vorhanden sind, werden Member in der folgenden Reihenfolge ausgeblendet: Die Klasseneinschränkung blendet Elemente in Schnittstelleneinschränkungen aus, die Elemente System.ValueType ausblenden (wenn Structure Einschränkung angegeben ist), in denen Member Objectausgeblendet werden. Wenn ein Element mit demselben Namen in mehr als einer Schnittstelleneinschränkung angezeigt wird, ist das Element nicht verfügbar (wie in der Vererbung mehrerer Schnittstellen), und der Typparameter muss in die gewünschte Schnittstelle umwandeln. Beispiel:
Class C1
Sub S1(x As Integer)
End Sub
End Class
Interface I1
Sub S1(x As Integer)
End Interface
Interface I2
Sub S1(y As Double)
End Interface
Module Test
Sub T1(Of T As {C1, I1, I2})()
Dim a As T
a.S1(10) ' Calls C1.S1, which is preferred
a.S1(10.10) ' Also calls C1.S1, class is still preferred
End Sub
Sub T2(Of T As {I1, I2})()
Dim a As T
a.S1(10) ' Error: Call is ambiguous between I1.S1, I2.S1
End Sub
End Module
Beim Angeben von Typparametern als Typargumente müssen die Typparameter die Einschränkungen der übereinstimmenden Typparameter erfüllen.
Class Base(Of T As Class)
End Class
Class Derived(Of V)
' Error: V does not satisfy the constraints of T
Inherits Base(Of V)
End Class
Werte eines eingeschränkten Typparameters können verwendet werden, um auf die Instanzmember zuzugreifen, einschließlich Instanzmethoden, die in der Einschränkung angegeben sind.
Interface IPrintable
Sub Print()
End Interface
Class Printer(Of V As IPrintable)
Sub PrintOne(v1 As V)
V1.Print()
End Sub
End Class
Typparameterabweichung
Ein Typparameter in einer Schnittstelle oder einer Delegattypdeklaration kann optional einen Varianzmodifizierer angeben. Typparameter mit Varianzmodifizierern beschränken, wie der Typparameter in der Schnittstelle oder dem Delegattyp verwendet werden kann, aber zulassen, dass eine generische Schnittstelle oder ein Stellvertretungstyp mit variantenkompatiblen Typargumenten in einen anderen generischen Typ konvertiert werden kann. Beispiel:
Class Base
End Class
Class Derived
Inherits Base
End Class
Module Test
Sub Main()
Dim x As IEnumerable(Of Derived) = ...
' OK, as IEnumerable(Of Base) is variant compatible
' with IEnumerable(Of Derived)
Dim y As IEnumerable(Of Base) = x
End Sub
End Module
Generische Schnittstellen mit Typparametern mit Varianzmodifizierern haben mehrere Einschränkungen:
Sie können keine Ereignisdeklaration enthalten, die eine Parameterliste angibt (aber eine benutzerdefinierte Ereignisdeklaration oder eine Ereignisdeklaration mit einem Delegattyp ist zulässig).
Sie können keinen geschachtelten Klassen-, Struktur- oder Enumerationstyp enthalten.
Hinweis: Diese Einschränkungen sind darauf zurückzuführen, dass Typen, die in generischen Typen geschachtelt sind, implizit die generischen Parameter ihres übergeordneten Elements kopieren. Bei geschachtelten Klassen, Strukturen oder Aufzählungstypen können diese Typen keine Varianzmodifizierer für ihre Typparameter haben. Im Falle einer Ereignisdeklaration mit einer Parameterliste könnte die generierte geschachtelte Delegatklasse verwirrende Fehler haben, wenn ein Typ, der in einer In Position verwendet wird (d. h. ein Parametertyp), tatsächlich an einer Out Position verwendet wird (d. h. der Typ des Ereignisses).
Ein Typparameter, der mit dem Modifizierer "Out" deklariert wird, ist kovariant. Informell kann ein kovarianter Typparameter nur an einer Ausgabeposition verwendet werden – d. h. einen Wert, der von der Schnittstelle oder dem Delegattyp zurückgegeben wird - und kann nicht an einer Eingabeposition verwendet werden. Ein Typ T gilt als kovariant , wenn:
Tist ein Klassen-, Struktur- oder Enumerationstyp.Tist nicht generischer Delegat- oder Schnittstellentyp.Tist ein Arraytyp, dessen Elementtyp kovariant gültig ist.Tist ein Typparameter, der nicht alsOutTypparameter deklariert wurde.Tist eine konstruierte Schnittstelle oder ein DelegattypX(Of P1,...,Pn)mit TypargumentenA1,...,An, z. B.:Wenn
Pier als Ausgabetypparameter deklariert wurde,Aiist er kovariant.Wenn
Pisie als In-Typ-Parameter deklariert wurde,Aiist dies kontravariant.
Folgendes muss in einer Schnittstelle oder einem Delegattyp kovariant gültig sein:
Die Basisschnittstelle einer Schnittstelle.
Der Rückgabetyp einer Funktion oder des Delegattyps.
Der Typ einer Eigenschaft, wenn ein
GetAccessor vorhanden ist.Der Typ eines beliebigen
ByRefParameters.
Beispiel:
Delegate Function D(Of Out T, U)(x As U) As T
Interface I1(Of Out T)
End Interface
Interface I2(Of Out T)
Inherits I1(Of T)
' OK, T is only used in an Out position
Function M1(x As I1(Of T)) As T
' Error: T is used in an In position
Function M2(x As T) As T
End Interface
Hinweis:
Out ist kein reserviertes Wort.
Ein Typparameter, der mit dem In-Modifizierer deklariert wird, ist kontravariant. Informell kann ein kontravarianter Typparameter nur an einer Eingabeposition verwendet werden – d. h. einen Wert, der an die Schnittstelle oder den Delegattyp übergeben wird - und kann nicht an einer Ausgabeposition verwendet werden. Ein Typ T gilt als gültig kontravariant , wenn:
Tist ein Klassen-, Struktur- oder Enumerationstyp.Tist ein nicht generischer Delegat oder Schnittstellentyp.Tist ein Arraytyp, dessen Elementtyp kontravariant gültig ist.Tist ein Typparameter, der nicht als In-Typ-Parameter deklariert wurde.Tist eine konstruierte Schnittstelle oder ein DelegattypX(Of P1,...,Pn)mit TypargumentenA1,...,An, z. B.:Wenn
Pisie alsOutTypparameter deklariert wurde,Aiist dies kontravariant.Wenn
Pisie alsInTypparameter deklariert wurde,Aiist dies kovariant.
Folgendes muss in einer Schnittstelle oder einem Delegattyp kontravariant gültig sein:
Der Typ eines Parameters.
Eine Typeinschränkung für einen Methodentypparameter.
Der Typ einer Eigenschaft, wenn sie über einen
SetAccessor verfügt.Der Typ eines Ereignisses.
Beispiel:
Delegate Function D(Of T, In U)(x As U) As T
Interface I1(Of In T)
End Interface
Interface I2(Of In T)
' OK, T is only used in an In position
Sub M1(x As I1(Of T))
' Error: T is used in an Out position
Function M2() As T
End Interface
In dem Fall, in dem ein Typ gültig sein muss, kontravariant und kovariant sein muss (z. B. eine Eigenschaft mit einem und Set einem Get Accessor oder einem ByRef Parameter), kann kein Variant-Typparameter verwendet werden.
Die Ko- und Kontraabweichung führen zu einem "Rautendeutigkeitsproblem". Beachten Sie den folgenden Code:
Class C
Implements IEnumerable(Of String)
Implements IEnumerable(Of Exception)
Public Function GetEnumerator1() As IEnumerator(Of String) _
Implements IEnumerable(Of String).GetEnumerator
Console.WriteLine("string")
End Function
Public Function GetEnumerator2() As IEnumerator(Of Exception) _
Implements IEnumerable(Of Execption).GetEnumerator
Console.WriteLine("exception")
End Function
End Class
Dim c As IEnumerable(Of Object) = New C
c.GetEnumerator()
Die Klasse C kann auf zwei Arten konvertiert IEnumerable(Of Object) werden, sowohl durch die kovariante Konvertierung von IEnumerable(Of String) und über die kovariante Konvertierung von IEnumerable(Of Exception). Die CLR gibt nicht an, von welchen der beiden Methoden aufgerufen c.GetEnumerator()wird. Im Allgemeinen gilt, wann immer eine Klasse deklariert wird, um eine kovariante Schnittstelle mit zwei verschiedenen generischen Argumenten zu implementieren, die einen gemeinsamen Supertyp haben (z. B. in diesem Fall String und Exception den gemeinsamen Supertyp Object), oder eine Klasse wird deklariert, um eine kontravariante Schnittstelle mit zwei verschiedenen generischen Argumenten zu implementieren, die einen gemeinsamen Untertyp aufweisen, dann kann die Mehrdeutigkeit auftreten. Der Compiler gibt eine Warnung für solche Deklarationen.
Visual Basic language spec