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.
Die beiden grundlegenden Kategorien von Typen in Visual Basic sind Werttypen und Referenztypen. Primitive Typen (außer Zeichenfolgen), Enumerationen und Strukturen sind Werttypen. Klassen, Zeichenfolgen, Standardmodule, Schnittstellen, Arrays und Delegaten sind Referenztypen.
Jeder Typ hat einen Standardwert, bei dem es sich um den Wert handelt, der Variablen dieses Typs bei der Initialisierung zugewiesen ist.
TypeName
: ArrayTypeName
| NonArrayTypeName
;
NonArrayTypeName
: SimpleTypeName
| NullableTypeName
;
SimpleTypeName
: QualifiedTypeName
| BuiltInTypeName
;
QualifiedTypeName
: Identifier TypeArguments? (Period IdentifierOrKeyword TypeArguments?)*
| 'Global' Period IdentifierOrKeyword TypeArguments?
(Period IdentifierOrKeyword TypeArguments?)*
;
TypeArguments
: OpenParenthesis 'Of' TypeArgumentList CloseParenthesis
;
TypeArgumentList
: TypeName ( Comma TypeName )*
;
BuiltInTypeName
: 'Object'
| PrimitiveTypeName
;
TypeModifier
: AccessModifier
| 'Shadows'
;
IdentifierModifiers
: NullableNameModifier? ArrayNameModifier?
;
Werttypen und Referenztypen
Obwohl Werttypen und Verweistypen in Bezug auf Deklarationssyntax und -verwendung ähnlich sein können, sind ihre Semantik unterschiedlich.
Referenztypen werden im Laufzeithap gespeichert; auf sie kann nur über einen Verweis auf diesen Speicher zugegriffen werden. Da auf Referenztypen immer über Verweise zugegriffen wird, wird die Lebensdauer von .NET Framework verwaltet. Ausstehende Verweise auf eine bestimmte Instanz werden nachverfolgt und die Instanz wird nur zerstört, wenn keine weiteren Verweise verbleiben. Eine Variable des Bezugstyps enthält einen Verweis auf einen Wert dieses Typs, einen Wert eines abgeleiteten Typs oder einen NULL-Wert. Ein Nullwert bezieht sich auf nichts; Es ist nicht möglich, etwas mit einem NULL-Wert zu tun, außer es zuzuweisen. Die Zuweisung zu einer Variablen eines Verweistyps erstellt eine Kopie des Verweises anstelle einer Kopie des referenzierten Werts. Bei einer Variablen eines Bezugstyps ist der Standardwert ein Nullwert.
Werttypen werden direkt im Stapel gespeichert, entweder innerhalb eines Arrays oder innerhalb eines anderen Typs; Auf deren Speicher kann nur direkt zugegriffen werden. Da Werttypen direkt innerhalb von Variablen gespeichert werden, wird ihre Lebensdauer durch die Lebensdauer der Variablen bestimmt, die sie enthält. Wenn der Speicherort, der eine Werttypinstanz enthält, zerstört wird, wird auch die Werttypinstanz zerstört. Werttypen werden immer direkt aufgerufen; Es ist nicht möglich, einen Verweis auf einen Werttyp zu erstellen. Das Verbieten eines solchen Verweises macht es unmöglich, auf eine Wertklasseninstanz zu verweisen, die zerstört wurde. Da Werttypen immer sind, enthält eine Variable eines Werttyps immer NotInheritableeinen Wert dieses Typs. Aus diesem Gründen kann der Wert eines Werttyps kein NULL-Wert sein, und es kann nicht auf ein Objekt eines abgeleiteten Typs verwiesen werden. Die Zuweisung zu einer Variablen eines Werttyps erstellt eine Kopie des zugewiesenen Werts. Bei einer Variablen eines Werttyps ist der Standardwert das Ergebnis der Initialisierung der einzelnen Variablenmememm des Typs auf den Standardwert.
Das folgende Beispiel zeigt den Unterschied zwischen Bezugstypen und Werttypen:
Class Class1
Public Value As Integer = 0
End Class
Module Test
Sub Main()
Dim val1 As Integer = 0
Dim val2 As Integer = val1
val2 = 123
Dim ref1 As Class1 = New Class1()
Dim ref2 As Class1 = ref1
ref2.Value = 123
Console.WriteLine("Values: " & val1 & ", " & val2)
Console.WriteLine("Refs: " & ref1.Value & ", " & ref2.Value)
End Sub
End Module
Die Ausgabe des Programms ist:
Values: 0, 123
Refs: 123, 123
Die Zuordnung zur lokalen Variablen wirkt sich nicht auf die lokale Variable val1 aus, da beide lokalen Variablen einen Werttyp (den TypInteger) aufweisen und jede lokale Variable val2 eines Werttyps über einen eigenen Speicher verfügt. Im Gegensatz dazu wirkt sich die Zuordnung ref2.Value = 123; auf das Objekt aus, das sowohl als ref2 auch ref1 auf das Referenzobjekt verweist.
Beachten Sie beim .NET Framework-Typsystem, dass sie alle von Referenztypen erben, obwohl Strukturen, Enumerationen und Grundtypen (mit Ausnahme von String) Werttypen sind. Strukturen und die Grundtypen erben vom Verweistyp System.ValueType, der von Object. Aufgezählte Typen erben vom Verweistyp System.Enum, der von System.ValueType.
Auf NULL festlegbare Werttypen
Bei Werttypen kann einem Typnamen ein ? Modifizierer hinzugefügt werden, der die nullwerte Version dieses Typs darstellt.
NullableTypeName
: NonArrayTypeName '?'
;
NullableNameModifier
: '?'
;
Ein nullabler Werttyp kann dieselben Werte wie die nicht nullable Version des Typs sowie den Nullwert enthalten. Daher wird für einen Nullwerttyp, der einer Variablen des Typs zugewiesen wird, Nothing der Wert der Variablen auf den Nullwert und nicht auf den Nullwert des Werttyps festgelegt. Beispiel:
Dim x As Integer = Nothing
Dim y As Integer? = Nothing
' Prints zero
Console.WriteLine(x)
' Prints nothing (because the value of y is the null value)
Console.WriteLine(y)
Eine Variable kann auch als Nullwerttyp deklariert werden, indem sie einen Null-Modifizierer für den Variablennamen eingibt. Aus Gründen der Übersichtlichkeit ist es nicht gültig, einen nullfähigen Typmodifizierer sowohl für einen Variablennamen als auch für einen Typnamen in derselben Deklaration zu verwenden. Da nullable Typen mit dem Typ System.Nullable(Of T)implementiert werden, ist der Typ T? synonym für den Typ System.Nullable(Of T), und die beiden Namen können austauschbar verwendet werden. Der ? Modifizierer kann nicht auf einen Typ gesetzt werden, der bereits nullfähig ist. Daher ist es nicht möglich, den Typ oder System.Nullable(Of Integer)?den Typ Integer?? zu deklarieren.
Ein nullabler Werttyp T? weist die Member System.Nullable(Of T) sowie alle Operatoren oder Konvertierungen auf, die vom zugrunde liegenden Typ T in den Typ T?aufgehoben werden. Das Heben von Operatoren und Konvertierungen aus dem zugrunde liegenden Typ ersetzt in den meisten Fällen nullable Werttypen für nicht nullable Werttypen. Dies ermöglicht viele der gleichen Konvertierungen und Vorgänge, die auch angewendet T werden können T? .
Schnittstellenimplementierung
Struktur- und Klassendeklarationen können deklarieren, dass sie einen Satz von Schnittstellentypen über eine oder Implements mehrere Klauseln implementieren.
TypeImplementsClause
: 'Implements' TypeImplements StatementTerminator
;
TypeImplements
: NonArrayTypeName ( Comma NonArrayTypeName )*
;
Alle in der Implements Klausel angegebenen Typen müssen Schnittstellen sein, und der Typ muss alle Member der Schnittstellen implementieren. Beispiel:
Interface ICloneable
Function Clone() As Object
End Interface
Interface IComparable
Function CompareTo(other As Object) As Integer
End Interface
Structure ListEntry
Implements ICloneable, IComparable
...
Public Function Clone() As Object Implements ICloneable.Clone
...
End Function
Public Function CompareTo(other As Object) As Integer _
Implements IComparable.CompareTo
...
End Function
End Structure
Ein Typ, der eine Schnittstelle implementiert, implementiert auch implizit alle Basisschnittstellen der Schnittstelle. Dies gilt auch, wenn der Typ nicht explizit alle Basisschnittstellen in der Implements Klausel auflistet. In diesem Beispiel implementiert die TextBox Struktur sowohl als ITextBoxauch IControl .
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Structure TextBox
Implements ITextBox
...
Public Sub Paint() Implements ITextBox.Paint
...
End Sub
Public Sub SetText(text As String) Implements ITextBox.SetText
...
End Sub
End Structure
Durch das Deklarieren, dass ein Typ eine Schnittstelle implementiert und selbst nichts im Deklarationsbereich des Typs deklariert. Daher ist es gültig, zwei Schnittstellen mit einer Methode mit demselben Namen zu implementieren.
Typen können einen Typparameter nicht eigenständig implementieren, obwohl er möglicherweise die Typparameter umfasst, die sich im Bereich befinden.
Class C1(Of V)
Implements V ' Error, can't implement type parameter directly
Implements IEnumerable(Of V) ' OK, not directly implementing
...
End Class
Generische Schnittstellen können mehrmals mit unterschiedlichen Typargumenten implementiert werden. Ein generischer Typ kann jedoch keine generische Schnittstelle mithilfe eines Typparameters implementieren, wenn sich der angegebene Typparameter (unabhängig von Typeinschränkungen) mit einer anderen Implementierung dieser Schnittstelle überlappen könnte. Beispiel:
Interface I1(Of T)
End Interface
Class C1
Implements I1(Of Integer)
Implements I1(Of Double) ' OK, no overlap
End Class
Class C2(Of T)
Implements I1(Of Integer)
Implements I1(Of T) ' Error, T could be Integer
End Class
Grundtypen
Die Grundtypen werden durch Schlüsselwörter identifiziert, bei denen es sich um Aliase für vordefinierte Typen im System Namespace handelt. Ein Grundtyp ist vollständig von dem Typ, den er aliase, nicht unterscheiden kann: Das Schreiben des reservierten Worts Byte entspricht genau dem Schreiben System.Byte. Grundtypen werden auch als systeminterne Typen bezeichnet.
PrimitiveTypeName
: NumericTypeName
| 'Boolean'
| 'Date'
| 'Char'
| 'String'
;
NumericTypeName
: IntegralTypeName
| FloatingPointTypeName
| 'Decimal'
;
IntegralTypeName
: 'Byte' | 'SByte' | 'UShort' | 'Short' | 'UInteger'
| 'Integer' | 'ULong' | 'Long'
;
FloatingPointTypeName
: 'Single' | 'Double'
;
Da ein Grundtyp einen regulären Typ aliast, verfügt jeder Grundtyp über Member. Hat z. B Integer . die Mitglieder in System.Int32deklariert. Literale können als Instanzen ihrer entsprechenden Typen behandelt werden.
Die Grundtypen unterscheiden sich von anderen Strukturtypen darin, dass sie bestimmte zusätzliche Vorgänge zulassen:
Primitive Typen ermöglichen das Erstellen von Werten durch Schreiben von Literalen. Beispiel:
123Iist ein Literal vom TypInteger.Es ist möglich, Konstanten der Grundtypen zu deklarieren.
Wenn die Operanden eines Ausdrucks alle primitiven Typkonstanten sind, ist es möglich, dass der Compiler den Ausdruck zur Kompilierungszeit auswerten kann. Ein solcher Ausdruck wird als konstanter Ausdruck bezeichnet.
Visual Basic definiert die folgenden Grundtypen:
Die integralen Werttypen
Byte(1-Byte-ganzzahl ohne Vorzeichen),SByte(1-Byte-ganzzahlUShort), (2-Byte-ganzzahl ohne Vorzeichen), (2-Byte-ganzzahlUInteger),Short(4-Byte ganzzahl ohne Vorzeichen),Integer(ULong8-Byte ganze Zahl) undLong(8-Byte-ganzzahl). Diese Typen sind zuSystem.Byte,System.SByte, ,System.UInt16,System.UInt32System.Int16,System.Int32,System.UInt64undSystem.Int64, bzw. zuzuordnen. Der Standardwert eines integralen Typs entspricht dem Literal0.Die Gleitkommawerttypen
Single(4-Byte-Gleitkomma) undDouble(8-Byte-Gleitkomma). Diese Typen werden jeweils zugeordnetSystem.SinglebzwSystem.Double. zugeordnet. Der Standardwert eines Gleitkommatyps entspricht dem Literal0.Der
DecimalTyp (16-Byte-Dezimalwert), der zugeordnet istSystem.Decimal. Der Standardwert der Dezimalzahl entspricht dem Literal0D.Der
BooleanWerttyp, der einen Wahrheitswert darstellt, in der Regel das Ergebnis einer relationalen oder logischen Operation. Das Literal ist vom TypSystem.Boolean. Der Standardwert desBooleanTyps entspricht dem LiteralFalse.Der
DateWerttyp, der ein Datum und/oder eine Uhrzeit darstellt und zugeordnetSystem.DateTimeist. Der Standardwert desDateTyps entspricht dem Literal# 01/01/0001 12:00:00AM #.Der
CharWerttyp, der ein einzelnes Unicode-Zeichen darstellt und dem zugeordnet istSystem.Char. Der Standardwert desCharTyps entspricht dem konstanten AusdruckChrW(0).Der
StringVerweistyp, der eine Abfolge von Unicode-Zeichen darstellt und der zugeordnet istSystem.String. Der Standardwert desStringTyps ist ein NULL-Wert.
Enumerationen
Enumerationen sind Werttypen, die von System.Enum einem grundtypintegralen Typ erben und symbolisch eine Gruppe von Werten darstellen.
EnumDeclaration
: Attributes? TypeModifier* 'Enum' Identifier
( 'As' NonArrayTypeName )? StatementTerminator
EnumMemberDeclaration+
'End' 'Enum' StatementTerminator
;
Bei einem Enumerationstyp Eist der Standardwert der vom Ausdruck CType(0, E)erzeugte Wert.
Der zugrunde liegende Typ einer Enumeration muss ein integraler Typ sein, der alle in der Enumeration definierten Enumerationswerte darstellen kann. Wenn ein zugrunde liegender Typ angegeben ist, muss es sich um einen UIntegerShortLongSByteUShortIntegerULongder entsprechenden Typen im System Namespace handeln.Byte Wenn kein zugrunde liegender Typ explizit angegeben ist, ist Integerder Standardwert .
Im folgenden Beispiel wird eine Aufzählung mit einem zugrunde liegenden Typ von Long:
Enum Color As Long
Red
Green
Blue
End Enum
Ein Entwickler kann einen zugrunde liegenden Typ von Long, wie im Beispiel, verwenden, um die Verwendung von Werten im Bereich von Long, aber nicht im Bereich von Integer, oder um diese Option für die Zukunft beizubehalten.
Enumerationsmm.
Die Member einer Aufzählung sind die in der Enumeration deklarierten Aufzählungswerte und die von der Klasse System.Enumgeerbten Member.
Der Bereich eines Enumerationsmememums ist der Textkörper der Enumerationsdeklaration. Dies bedeutet, dass außerhalb einer Enumerationsdeklaration immer ein Enumerationselement qualifiziert sein muss (es sei denn, der Typ wird über einen Namespaceimport in einen Namespace importiert).
Die Deklarationsreihenfolge für Enumerationsmememerdeklarationen ist wichtig, wenn konstanten Ausdruckswerte weggelassen werden. Enumerationsmember haben Public implizit nur Zugriff. Für Enumerationsmemberdeklarationen sind keine Zugriffsmodifizierer zulässig.
EnumMemberDeclaration
: Attributes? Identifier ( Equals ConstantExpression )? StatementTerminator
;
Enumerationswerte
Die aufgezählten Werte in einer Enumerationselementliste werden als Konstanten deklariert, die als zugrunde liegender Enumerationstyp eingegeben werden, und sie können überall dort angezeigt werden, wo Konstanten erforderlich sind. Eine Enumerationsmememmdefinition mit = dem zugeordneten Element den wert, der durch den Konstantenausdruck angegeben wird. Der konstante Ausdruck muss einen integralen Typ auswerten, der implizit in den zugrunde liegenden Typ konvertierbar ist und sich innerhalb des Wertebereichs befinden muss, der durch den zugrunde liegenden Typ dargestellt werden kann. Das folgende Beispiel ist fehlerhaft, da die Konstantenwerte 1.5, 2.3und 3.3 nicht implizit in den zugrunde liegenden integralen Typ Long mit strenger Semantik konvertierbar sind.
Option Strict On
Enum Color As Long
Red = 1.5
Green = 2.3
Blue = 3.3
End Enum
Mehrere Enumerationsmermber können denselben zugeordneten Wert gemeinsam nutzen, wie unten dargestellt:
Enum Color
Red
Green
Blue
Max = Blue
End Enum
Das Beispiel zeigt eine Aufzählung mit zwei Enumerationselementen ( Blue und Max -) mit demselben zugeordneten Wert.
Wenn die erste Enumerationswertdefinition in der Enumeration keinen Initialisierer aufweist, lautet 0der Wert der entsprechenden Konstante. Eine Enumerationswertdefinition ohne Initialisierer gibt dem Enumerator den durch Erhöhen des Werts des vorherigen Enumerationswerts abgerufenen Wert um 1. Dieser erhöhte Wert muss sich innerhalb des Wertebereichs befinden, der durch den zugrunde liegenden Typ dargestellt werden kann.
Enum Color
Red
Green = 10
Blue
End Enum
Module Test
Sub Main()
Console.WriteLine(StringFromColor(Color.Red))
Console.WriteLine(StringFromColor(Color.Green))
Console.WriteLine(StringFromColor(Color.Blue))
End Sub
Function StringFromColor(c As Color) As String
Select Case c
Case Color.Red
Return String.Format("Red = " & CInt(c))
Case Color.Green
Return String.Format("Green = " & CInt(c))
Case Color.Blue
Return String.Format("Blue = " & CInt(c))
Case Else
Return "Invalid color"
End Select
End Function
End Module
Im obigen Beispiel werden die Enumerationswerte und die zugehörigen Werte gedruckt. Die Ausgabe lautet:
Red = 0
Green = 10
Blue = 11
Die Gründe für die Werte sind wie folgt:
Der Enumerationswert
Redwird dem Wert0automatisch zugewiesen (da er keinen Initialisierer hat und das erste Enumerationswertelement ist).Der Enumerationswert
Greenwird explizit dem Wert10übergeben.Dem Enumerationswert
Bluewird automatisch der Wert 1 zugewiesen, der größer als der Enumerationswert ist, dem textlich vorausgeht.
Der konstante Ausdruck verwendet möglicherweise nicht direkt oder indirekt den Wert seines eigenen zugeordneten Enumerationswerts (d. a. Zirkelzahl im konstanten Ausdruck ist nicht zulässig). Das folgende Beispiel ist ungültig, da die Deklarationen von A und B sind zirkulär.
Enum Circular
A = B
B
End Enum
A hängt von B explizit ab und B hängt implizit davon ab A .
Klassen
Eine Klasse ist eine Datenstruktur, die Datenmember (Konstanten, Variablen und Ereignisse), Funktionsmember (Methoden, Eigenschaften, Indexer, Operatoren und Konstruktoren) und geschachtelte Typen enthalten kann. Klassen sind Referenztypen.
ClassDeclaration
: Attributes? ClassModifier* 'Class' Identifier TypeParameterList? StatementTerminator
ClassBase?
TypeImplementsClause*
ClassMemberDeclaration*
'End' 'Class' StatementTerminator
;
ClassModifier
: TypeModifier
| 'MustInherit'
| 'NotInheritable'
| 'Partial'
;
Das folgende Beispiel zeigt eine Klasse, die jede Art von Member enthält:
Class AClass
Public Sub New()
Console.WriteLine("Constructor")
End Sub
Public Sub New(value As Integer)
MyVariable = value
Console.WriteLine("Constructor")
End Sub
Public Const MyConst As Integer = 12
Public MyVariable As Integer = 34
Public Sub MyMethod()
Console.WriteLine("MyClass.MyMethod")
End Sub
Public Property MyProperty() As Integer
Get
Return MyVariable
End Get
Set (value As Integer)
MyVariable = value
End Set
End Property
Default Public Property Item(index As Integer) As Integer
Get
Return 0
End Get
Set (value As Integer)
Console.WriteLine("Item(" & index & ") = " & value)
End Set
End Property
Public Event MyEvent()
Friend Class MyNestedClass
End Class
End Class
Das folgende Beispiel zeigt die Verwendung dieser Member:
Module Test
' Event usage.
Dim WithEvents aInstance As AClass
Sub Main()
' Constructor usage.
Dim a As AClass = New AClass()
Dim b As AClass = New AClass(123)
' Constant usage.
Console.WriteLine("MyConst = " & AClass.MyConst)
' Variable usage.
a.MyVariable += 1
Console.WriteLine("a.MyVariable = " & a.MyVariable)
' Method usage.
a.MyMethod()
' Property usage.
a.MyProperty += 1
Console.WriteLine("a.MyProperty = " & a.MyProperty)
a(1) = 1
' Event usage.
aInstance = a
End Sub
Sub MyHandler() Handles aInstance.MyEvent
Console.WriteLine("Test.MyHandler")
End Sub
End Module
Es gibt zwei klassenspezifische Modifizierer MustInherit und NotInheritable. Es ist ungültig, beide anzugeben.
Klassenbasisspezifikation
Eine Klassendeklaration kann eine Basistypspezifikation enthalten, die den direkten Basistyp der Klasse definiert.
ClassBase
: 'Inherits' NonArrayTypeName StatementTerminator
;
Wenn eine Klassendeklaration keinen expliziten Basistyp aufweist, ist der direkte Basistyp implizit Object. Beispiel:
Class Base
End Class
Class Derived
Inherits Base
End Class
Basistypen können selbst kein Typparameter sein, obwohl sie die Typparameter umfassen kann, die sich im Bereich befinden.
Class C1(Of V)
End Class
Class C2(Of V)
Inherits V ' Error, type parameter used as base class
End Class
Class C3(Of V)
Inherits C1(Of V) ' OK: not directly inheriting from V.
End Class
Klassen können nur von Object und Klassen abgeleitet werden. Es ist ungültig, damit eine Klasse von , , , oder System.ArraySystem.DelegateSystem.MulticastDelegate . System.EnumSystem.ValueType Eine generische Klasse kann nicht von System.Attribute oder von einer Klasse abgeleitet werden, die von ihr abgeleitet wird.
Jede Klasse hat genau eine direkte Basisklasse, und die Zirkelität in der Ableitung ist verboten. Es ist nicht möglich, von einer NotInheritable Klasse abzuleiten, und die Barrierefreiheitsdomäne der Basisklasse muss mit der oder einer Obermenge der Barrierefreiheitsdomäne der Klasse selbst übereinstimmen.
Klassenm.
Die Member einer Klasse bestehen aus den Membern, die durch ihre Klassenmemberdeklarationen eingeführt wurden, und die Member, die von ihrer direkten Basisklasse geerbt wurden.
ClassMemberDeclaration
: NonModuleDeclaration
| EventMemberDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
| OperatorDeclaration
;
Eine Klassenmememerdeklaration kann über , Protected, , FriendProtected Friendoder Private Zugriff verfügenPublic. Wenn eine Klassenmembezeichner-Deklaration keinen Zugriffsmodifizierer enthält, wird standardmäßig auf die Deklaration Public zugegriffen, es sei denn, es handelt sich um eine Variabledeklaration. In diesem Fall wird Private standardmäßig darauf zugegriffen.
Der Bereich eines Klassenelements ist der Klassentext, in dem die Memberdeklaration auftritt, sowie die Einschränkungsliste dieser Klasse (sofern es generisch ist und Einschränkungen aufweist). Wenn das Mitglied Zugriff hat Friend , erstreckt sich der Bereich auf den Klassentext einer abgeleiteten Klasse im selben Programm oder auf eine Assembly, die Zugriff gewährt Friend hat, und wenn das Mitglied Publicden Klassentext einer abgeleiteten Klasse in einem beliebigen Programm hat, Protectedoder Protected Friend zugriff hat.
Strukturen
Strukturen sind Werttypen, die von System.ValueType. Strukturen ähneln Klassen, in denen sie Datenstrukturen darstellen, die Datenmmber und Funktionsmber enthalten können. Im Gegensatz zu Klassen erfordern Strukturen jedoch keine Heapzuordnung.
StructureDeclaration
: Attributes? StructureModifier* 'Structure' Identifier
TypeParameterList? StatementTerminator
TypeImplementsClause*
StructMemberDeclaration*
'End' 'Structure' StatementTerminator
;
StructureModifier
: TypeModifier
| 'Partial'
;
Im Fall von Klassen ist es möglich, dass zwei Variablen auf dasselbe Objekt verweisen, und somit für Vorgänge auf eine Variable, die auf das objekt verwiesen wird, durch die andere Variable beeinflusst werden kann. Bei Strukturen verfügen die Variablen jeweils über eine eigene Kopie der Nicht-DatenShared , sodass Vorgänge auf einer Seite nicht betroffen sind, wie das folgende Beispiel veranschaulicht:
Structure Point
Public x, y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
End Structure
Bei der obigen Deklaration gibt der folgende Code den Wert aus 10:
Module Test
Sub Main()
Dim a As New Point(10, 10)
Dim b As Point = a
a.x = 100
Console.WriteLine(b.x)
End Sub
End Module
Die Zuordnung von a zu b erstellt eine Kopie des Werts, und b ist somit von der Zuordnung zu a.x unberührt. Wäre Point stattdessen als Klasse deklariert worden, wäre die Ausgabe 100, weil a und b auf dasselbe Objekt verweisen würden.
Strukturm.
Die Member einer Struktur sind die Elemente, die durch ihre Strukturmemberdeklarationen und von den Von der Struktur geerbten System.ValueTypeMembern eingeführt werden.
StructMemberDeclaration
: NonModuleDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| EventMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
| OperatorDeclaration
;
Jede Struktur verfügt implizit über einen Public parameterlosen Instanzkonstruktor, der den Standardwert der Struktur erzeugt. Daher ist es für eine Strukturtypdeklaration nicht möglich, einen parameterlosen Instanzkonstruktor zu deklarieren. Ein Strukturtyp kann jedoch parametrisierte Instanzkonstruktoren deklarieren, wie im folgenden Beispiel gezeigt:
Structure Point
Private x, y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
End Structure
In Anbetracht der obigen Deklaration erstellen die folgenden Anweisungen eine Point mit x und y initialisiert auf Null.
Dim p1 As Point = New Point()
Dim p2 As Point = New Point(0, 0)
Da Strukturen direkt ihre Feldwerte (anstelle von Verweisen auf diese Werte) enthalten, können Strukturen keine Felder enthalten, die direkt oder indirekt auf sich selbst verweisen. Der folgende Code ist beispielsweise ungültig:
Structure S1
Dim f1 As S2
End Structure
Structure S2
' This would require S1 to contain itself.
Dim f1 As S1
End Structure
Normalerweise verfügt eine Strukturmemberdeklaration möglicherweise nur über Public, Friendoder Private zugriff, aber wenn Elemente überschrieben werden, von Objectdenen geerbt wird, Protected und Protected Friend der Zugriff kann auch verwendet werden. Wenn eine Strukturmembezeichnerdeklaration keinen Zugriffsmodifizierer enthält, wird standardmäßig auf Public die Deklaration zugegriffen. Der Bereich eines von einer Struktur deklarierten Elements ist der Strukturtext, in dem die Deklaration auftritt, sowie die Einschränkungen dieser Struktur (sofern es generisch war und Einschränkungen hatte).
Standardmodule
Ein Standardmodul ist ein Typ, dessen Member implizit Shared und auf den Deklarationsbereich des Standardmoduls beschränkt sind, der den Namespace enthält, und nicht nur auf die Standardmoduldeklaration selbst. Standardmodule werden möglicherweise nie instanziiert. Fehler beim Deklarieren einer Variablen eines Standardmodultyps.
ModuleDeclaration
: Attributes? TypeModifier* 'Module' Identifier StatementTerminator
ModuleMemberDeclaration*
'End' 'Module' StatementTerminator
;
Ein Mitglied eines Standardmoduls verfügt über zwei vollqualifizierte Namen, eines ohne den Standardmodulnamen und einen mit dem Standardmodulnamen. Mehrere Standardmodule in einem Namespace können ein Element mit einem bestimmten Namen definieren; Nicht qualifizierte Verweise auf den Namen außerhalb eines moduls sind mehrdeutig. Beispiel:
Namespace N1
Module M1
Sub S1()
End Sub
Sub S2()
End Sub
End Module
Module M2
Sub S2()
End Sub
End Module
Module M3
Sub Main()
S1() ' Valid: Calls N1.M1.S1.
N1.S1() ' Valid: Calls N1.M1.S1.
S2() ' Not valid: ambiguous.
N1.S2() ' Not valid: ambiguous.
N1.M2.S2() ' Valid: Calls N1.M2.S2.
End Sub
End Module
End Namespace
Ein Modul kann nur in einem Namespace deklariert werden und darf nicht in einem anderen Typ geschachtelt werden. Standardmodule implementieren möglicherweise keine Schnittstellen, sie leiten implizit von ihnen ab Object, und sie verfügen nur Shared über Konstruktoren.
Elemente des Standardmoduls
Die Member eines Standardmoduls sind die Elemente, die von ihren Memberdeklarationen und den von ihm geerbten ObjectMembern eingeführt werden. Standardmodule können über einen beliebigen Membertyp mit Ausnahme von Instanzkonstruktoren verfügen. Alle Standardmäßigen Modultypmmber sind implizit Shared.
ModuleMemberDeclaration
: NonModuleDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| EventMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
;
In der Regel verfügt eine Standardmodulmemberdeklaration möglicherweise nur über Public, Friendoder Private zugriff, aber beim überschreiben von Objectgeerbten Membern können die Protected Modifizierer und Protected Friend Zugriffsmodifizierer angegeben werden. Wenn eine Standardmodulmembezeichnerdeklaration keinen Zugriffsmodifizierer enthält, wird standardmäßig auf die Deklaration Public zugegriffen, es sei denn, es handelt sich um eine Variable, auf die standardmäßig zugegriffen werden soll Private .
Wie bereits erwähnt, ist der Umfang eines Standardmodulelements die Deklaration, die die Standardmoduldeklaration enthält. Von dieser speziellen Bereichsdefinition geerbte Object Elemente sind nicht enthalten. Diese Member haben keinen Bereich und müssen immer mit dem Namen des Moduls qualifiziert werden. Wenn das Mitglied Zugriff hat Friend , erstreckt sich sein Bereich nur auf Namespacemember, die im selben Programm oder in den assemblys deklariert wurden, die Zugriff gewährt Friend haben.
Schnittstellen
Schnittstellen sind Referenztypen, die andere Typen implementieren, um sicherzustellen, dass sie bestimmte Methoden unterstützen. Eine Schnittstelle wird nie direkt erstellt und hat keine tatsächliche Darstellung . Andere Typen müssen in einen Schnittstellentyp konvertiert werden. Eine Schnittstelle definiert einen Vertrag. Eine Klasse oder Struktur, die eine Schnittstelle implementiert, muss ihren Vertrag einhalten.
InterfaceDeclaration
: Attributes? TypeModifier* 'Interface' Identifier
TypeParameterList? StatementTerminator
InterfaceBase*
InterfaceMemberDeclaration*
'End' 'Interface' StatementTerminator
;
Das folgende Beispiel zeigt eine Schnittstelle, die eine Standardeigenschaft Item, ein Ereignis E, eine Methode Fund eine Eigenschaft Penthält:
Interface IExample
Default Property Item(index As Integer) As String
Event E()
Sub F(value As Integer)
Property P() As String
End Interface
Schnittstellen können mehrere Vererbungen verwenden. Im folgenden Beispiel erbt die Schnittstelle IComboBox von beiden ITextBox und IListBox:
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Interface IListBox
Inherits IControl
Sub SetItems(items() As String)
End Interface
Interface IComboBox
Inherits ITextBox, IListBox
End Interface
Klassen und Strukturen können mehrere Schnittstellen implementieren. Im folgenden Beispiel wird die Klasse EditBox von der Klasse Control abgeleitet und implementiert sowohl als IDataBoundauchIControl:
Interface IDataBound
Sub Bind(b As Binder)
End Interface
Public Class EditBox
Inherits Control
Implements IControl, IDataBound
Public Sub Paint() Implements IControl.Paint
...
End Sub
Public Sub Bind(b As Binder) Implements IDataBound.Bind
...
End Sub
End Class
Schnittstellenvererbung
Die Basisschnittstellen einer Schnittstelle sind die expliziten Basisschnittstellen und ihre Basisschnittstellen. Anders ausgedrückt: Der Satz von Basisschnittstellen ist der vollständige transitive Abschluss der expliziten Basisschnittstellen, ihrer expliziten Basisschnittstellen usw. Wenn eine Schnittstellendeklaration keine explizite Schnittstellenbasis hat, gibt es keine Basisschnittstelle für den Typ - Schnittstellen erben Object nicht von (obwohl sie eine Erweiterung in Object).
InterfaceBase
: 'Inherits' InterfaceBases StatementTerminator
;
InterfaceBases
: NonArrayTypeName ( Comma NonArrayTypeName )*
;
Im folgenden Beispiel sind die Basisschnittstellen IComboBox , und IListBoxITextBox.IControl
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Interface IListBox
Inherits IControl
Sub SetItems(items() As String)
End Interface
Interface IComboBox
Inherits ITextBox, IListBox
End Interface
Eine Schnittstelle erbt alle Member ihrer Basisschnittstellen. Anders ausgedrückt: Die Schnittstelle IComboBox oben erbt die Member SetText und SetItems sowie Paint.
Eine Klasse oder Struktur, die eine Schnittstelle implementiert, implementiert auch implizit alle Basisschnittstellen der Schnittstelle.
Wenn eine Schnittstelle mehrmals im transitiven Schließen der Basisschnittstellen angezeigt wird, trägt sie nur einmal zur abgeleiteten Schnittstelle bei. Ein Typ, der die abgeleitete Schnittstelle implementiert, muss nur einmal die Methoden der multiplizierten basisschnittstelle implementieren. Im folgenden Beispiel Paint muss nur einmal implementiert werden, auch wenn die Klasse implementiert IComboBox und IControl.
Class ComboBox
Implements IControl, IComboBox
Sub SetText(text As String) Implements IComboBox.SetText
End Sub
Sub SetItems(items() As String) Implements IComboBox.SetItems
End Sub
Sub Print() Implements IComboBox.Paint
End Sub
End Class
Eine Inherits Klausel hat keine Auswirkung auf andere Inherits Klauseln. Im folgenden Beispiel IDerived muss der Name von INested "with" IBasequalifiziert werden.
Interface IBase
Interface INested
Sub Nested()
End Interface
Sub Base()
End Interface
Interface IDerived
Inherits IBase, INested ' Error: Must specify IBase.INested.
End Interface
Die Barrierefreiheitsdomäne einer Basisschnittstelle muss mit der Barrierefreiheitsdomäne der Schnittstelle selbst identisch sein oder eine Obermenge der Barrierefreiheitsdomäne der Schnittstelle selbst sein.
Schnittstellenm.
Die Member einer Schnittstelle bestehen aus den Memberdeklarationen und den Membern, die von ihren Basisschnittstellen geerbt wurden.
InterfaceMemberDeclaration
: NonModuleDeclaration
| InterfaceEventMemberDeclaration
| InterfaceMethodMemberDeclaration
| InterfacePropertyMemberDeclaration
;
Obwohl Schnittstellen keine Member Objecterben, da jede Klasse oder Struktur, die eine Schnittstelle implementiert, erbt Object, werden die Member von Object, einschließlich Erweiterungsmethoden, als Member einer Schnittstelle betrachtet und können direkt auf einer Schnittstelle aufgerufen werden, ohne dass eine Umwandlung erforderlich ist Object. Beispiel:
Interface I1
End Interface
Class C1
Implements I1
End Class
Module Test
Sub Main()
Dim i As I1 = New C1()
Dim h As Integer = i.GetHashCode()
End Sub
End Module
Elemente einer Schnittstelle mit demselben Namen wie Elemente von Object implizit Schattenmitgliedern Object . Nur geschachtelte Typen, Methoden, Eigenschaften und Ereignisse können Member einer Schnittstelle sein. Methoden und Eigenschaften verfügen möglicherweise nicht über einen Textkörper. Schnittstellenmber sind implizit Public und können keinen Zugriffsmodifizierer angeben. Der Bereich eines in einer Schnittstelle deklarierten Elements ist der Schnittstellentext, in dem die Deklaration auftritt, sowie die Einschränkungsliste dieser Schnittstelle (sofern es generisch ist und Einschränkungen aufweist).
Felder
Ein Array ist ein Bezugstyp, der Variablen enthält, auf die über Indizes zugegriffen wird, die auf 1:1-Weise mit der Reihenfolge der Variablen im Array übereinstimmen. Die in einem Array enthaltenen Variablen, auch als Elemente des Arrays bezeichnet, müssen alle denselben Typ aufweisen, und dieser Typ wird als Elementtyp des Arrays bezeichnet.
ArrayTypeName
: NonArrayTypeName ArrayTypeModifiers
;
ArrayTypeModifiers
: ArrayTypeModifier+
;
ArrayTypeModifier
: OpenParenthesis RankList? CloseParenthesis
;
RankList
: Comma*
;
ArrayNameModifier
: ArrayTypeModifiers
| ArraySizeInitializationModifier
;
Die Elemente eines Arrays treten beim Erstellen einer Arrayinstanz auf und werden nicht mehr vorhanden, wenn die Arrayinstanz zerstört wird. Jedes Element eines Arrays wird auf den Standardwert seines Typs initialisiert. Der Typ System.Array ist der Basistyp aller Arraytypen und kann nicht instanziiert werden. Jeder Arraytyp erbt die vom System.Array Typ deklarierten Member und ist in ihn (und Object) konvertierbar. Ein eindimensionaler Arraytyp mit Element T implementiert auch die Schnittstellen System.Collections.Generic.IList(Of T) und IReadOnlyList(Of T); wenn T es sich um einen Verweistyp handelt, implementiert IList(Of U) der Arraytyp auch und IReadOnlyList(Of U) für alle U , die eine Erweiterungsverweiskonvertierung aufweisen T.
Ein Array verfügt über einen Rang , der die Anzahl der Indizes bestimmt, die jedem Arrayelement zugeordnet sind. Der Rang eines Arrays bestimmt die Anzahl der Dimensionen des Arrays. Beispielsweise wird ein Array mit einem Rang von 1 als eindimensionales Array bezeichnet, und ein Array mit einem Rang größer als ein Array wird als mehrdimensionales Array bezeichnet.
Im folgenden Beispiel wird ein eindimensionales Array mit ganzzahligen Werten erstellt, die Arrayelemente initialisiert und dann jede dieser Elemente gedruckt:
Module Test
Sub Main()
Dim arr(5) As Integer
Dim i As Integer
For i = 0 To arr.Length - 1
arr(i) = i * i
Next i
For i = 0 To arr.Length - 1
Console.WriteLine("arr(" & i & ") = " & arr(i))
Next i
End Sub
End Module
Das Programm gibt Folgendes aus:
arr(0) = 0
arr(1) = 1
arr(2) = 4
arr(3) = 9
arr(4) = 16
arr(5) = 25
Jede Dimension eines Arrays weist eine zugeordnete Länge auf. Dimensionslängen sind nicht Teil des Typs des Arrays, sondern werden eingerichtet, wenn zur Laufzeit eine Instanz des Arraytyps erstellt wird. Die Länge einer Dimension bestimmt den gültigen Indizesbereich für diese Dimension: Für eine Dimension der Länge Nkönnen Indizes zwischen Null und N-1Indizes liegen. Wenn eine Dimension der Länge Null ist, gibt es keine gültigen Indizes für diese Dimension. Die Gesamtanzahl der Elemente in einem Array ist das Produkt der Längen jeder Dimension im Array. Wenn eine der Dimensionen eines Arrays eine Länge von Null hat, wird das Array als leer angegeben. Der Elementtyp eines Arrays kann ein beliebiger Typ sein.
Arraytypen werden durch Hinzufügen eines Modifizierers zu einem vorhandenen Typnamen angegeben. Der Modifizierer besteht aus einer linken Klammer, einer Gruppe von Null oder mehr Kommas und einer rechten Klammer. Der geänderte Typ ist der Elementtyp des Arrays, und die Anzahl der Dimensionen ist die Anzahl der Kommas plus eins. Wenn mehrere Modifizierer angegeben sind, ist der Elementtyp des Arrays ein Array. Die Modifizierer werden von links nach rechts gelesen, wobei der modifizierer ganz links das äußerste Array ist. Im Beispiel
Module Test
Dim arr As Integer(,)(,,)()
End Module
Der Elementtyp von arr ist ein zweidimensionales Array von dreidimensionalen Arrays von eindimensionalen Arrays von Integer.
Eine Variable kann auch als Arraytyp deklariert werden, indem sie einen Arraytypmodifizierer oder einen Modifizierer für die Initialisierung der Arraygröße für den Variablennamen einfügen. In diesem Fall ist der Arrayelementtyp der Typ, der in der Deklaration angegeben wird, und die Arrayabmessungen werden durch den Variablennamenmodifizierer bestimmt. Aus Gründen der Übersichtlichkeit ist es ungültig, einen Arraytypmodifizierer sowohl für einen Variablennamen als auch für einen Typnamen in derselben Deklaration zu verwenden.
Das folgende Beispiel zeigt eine Vielzahl von lokalen Variablendeklarationen, die Arraytypen Integer als Elementtyp verwenden:
Module Test
Sub Main()
Dim a1() As Integer ' Declares 1-dimensional array of integers.
Dim a2(,) As Integer ' Declares 2-dimensional array of integers.
Dim a3(,,) As Integer ' Declares 3-dimensional array of integers.
Dim a4 As Integer() ' Declares 1-dimensional array of integers.
Dim a5 As Integer(,) ' Declares 2-dimensional array of integers.
Dim a6 As Integer(,,) ' Declares 3-dimensional array of integers.
' Declare 1-dimensional array of 2-dimensional arrays of integers
Dim a7()(,) As Integer
' Declare 2-dimensional array of 1-dimensional arrays of integers.
Dim a8(,)() As Integer
Dim a9() As Integer() ' Not allowed.
End Sub
End Module
Ein Arraytypname-Modifizierer wird auf alle Klammern erweitert, die darauf folgen. Dies bedeutet, dass in situationen, in denen eine Gruppe von Argumenten, die in Klammern eingeschlossen sind, nach einem Typnamen zulässig ist, die Argumente für einen Arraytypnamen nicht angegeben werden können. Beispiel:
Module Test
Sub Main()
' This calls the Integer constructor.
Dim x As New Integer(3)
' This declares a variable of Integer().
Dim y As Integer()
' This gives an error.
' Array sizes can not be specified in a type name.
Dim z As Integer()(3)
End Sub
End Module
Im letzten Fall (3) wird anstelle einer Gruppe von Konstruktorargumenten als Teil des Typnamens interpretiert.
Delegierte
Ein Delegat ist ein Verweistyp, der auf eine Shared Methode eines Typs oder auf eine Instanzmethode eines Objekts verweist.
DelegateDeclaration
: Attributes? TypeModifier* 'Delegate' MethodSignature StatementTerminator
;
MethodSignature
: SubSignature
| FunctionSignature
;
Die nächstliegende Entsprechung eines Delegaten in anderen Sprachen ist ein Funktionszeiger, während ein Funktionszeiger nur auf Funktionen verweisen Shared kann, kann ein Delegat sowohl Shared als auch auf Instanzmethoden verweisen. Im letzteren Fall speichert der Delegate nicht nur einen Verweis auf den Einstiegspunkt der Methode, sondern auch einen Verweis auf die Objektinstanz, mit der die Methode aufgerufen werden soll.
Die Delegatdeklaration verfügt möglicherweise nicht über eine Handles Klausel, eine Implements Klausel, einen Methodentext oder ein End Konstrukt. Die Parameterliste der Delegatdeklaration darf keine Parameter oder ParamArray Parameter enthaltenOptional. Die Barrierefreiheitsdomäne des Rückgabetyps und der Parametertypen muss mit der Barrierefreiheitsdomäne des Delegaten selbst übereinstimmen oder eine Obermenge der Barrierefreiheitsdomäne sein.
Die Mitglieder einer Stellvertretung sind die Mitglieder, die von der Klasse System.Delegategeerbt werden. Eine Stellvertretung definiert außerdem die folgenden Methoden:
Ein Konstruktor, der zwei Parameter verwendet, einen typ
Objectund einen vom TypSystem.IntPtr.Eine
InvokeMethode, die dieselbe Signatur wie der Delegat hat.Eine
BeginInvokeMethode, deren Signatur die Stellvertretungssignatur ist, mit drei Unterschieden. Zunächst wird der Rückgabetyp inSystem.IAsyncResult. Zweitens werden zwei zusätzliche Parameter am Ende der Parameterliste hinzugefügt: der erste typSystem.AsyncCallbackund der zweite vom TypObject. Und schließlich werden alleByRefParameter geändert.ByValEine
EndInvokeMethode, deren Rückgabetyp mit dem Delegat identisch ist. Die Parameter der Methode sind nur die Stellvertretungsparameter, die genau die Parameter sindByRef, in der gleichen Reihenfolge wie in der Stellvertretungssignatur. Zusätzlich zu diesen Parametern gibt es einen zusätzlichen Parameter vom TypSystem.IAsyncResultam Ende der Parameterliste.
Es gibt drei Schritte beim Definieren und Verwenden von Stellvertretungen: Deklaration, Instanziierung und Aufruf.
Stellvertretungen werden mithilfe der Deklarationssyntax der Stellvertretung deklariert. Im folgenden Beispiel wird ein Delegat deklariert SimpleDelegate , der keine Argumente akzeptiert:
Delegate Sub SimpleDelegate()
Im nächsten Beispiel wird eine SimpleDelegate Instanz erstellt und dann sofort aufgerufen:
Module Test
Sub F()
System.Console.WriteLine("Test.F")
End Sub
Sub Main()
Dim d As SimpleDelegate = AddressOf F
d()
End Sub
End Module
Es gibt keinen großen Punkt beim Instanziieren eines Delegaten für eine Methode und dann sofort das Aufrufen über den Delegaten, da es einfacher wäre, die Methode direkt aufzurufen. Stellvertretungen zeigen ihre Nützlichkeit, wenn ihre Anonymität verwendet wird. Das nächste Beispiel zeigt eine MultiCall Methode, die wiederholt eine SimpleDelegate Instanz aufruft:
Sub MultiCall(d As SimpleDelegate, count As Integer)
Dim i As Integer
For i = 0 To count - 1
d()
Next i
End Sub
Es ist nicht wichtig für die MultiCall Methode, was die Zielmethode für die SimpleDelegate Methode ist, welche Barrierefreiheit diese Methode hat oder ob die Methode ist Shared oder nicht. Alles, was wichtig ist, ist, dass die Signatur der Zielmethode mit SimpleDelegatekompatibel ist.
Partial types (Partielle Typen)
Klassen- und Strukturdeklarationen können partielle Deklarationen sein. Eine Teildeklaration kann den deklarierten Typ innerhalb der Deklaration vollständig beschreiben oder nicht vollständig beschreiben. Stattdessen kann die Deklaration des Typs über mehrere Teildeklarationen innerhalb des Programms verteilt werden; Partielle Typen können nicht über Programmgrenzen hinweg deklariert werden. Eine partielle Typdeklaration gibt den Partial Modifizierer für die Deklaration an. Anschließend werden alle anderen Deklarationen im Programm für einen Typ mit demselben vollqualifizierten Namen zusammen mit der teilweisen Deklaration zur Kompilierung zusammengeführt, um eine einzelne Typdeklaration zu bilden. Der folgende Code deklariert z. B. eine einzelne Klasse Test mit Membern Test.C1 und Test.C2.
a.vb:
Public Partial Class Test
Public Sub S1()
End Sub
End Class
b.vb:
Public Class Test
Public Sub S2()
End Sub
End Class
Bei der Kombination von partiellen Typdeklarationen muss mindestens eine der Deklarationen über einen Partial Modifizierer verfügen, andernfalls tritt ein Kompilierungsfehler auf.
Hinweis: Obwohl es möglich ist, nur eine Deklaration unter vielen Teildeklarationen anzugeben Partial , ist es besser, sie für alle Teildeklarationen anzugeben. In der Situation, in der eine Teildeklaration sichtbar ist, aber mindestens eine Teildeklaration ausgeblendet ist (z. B. beim Erweitern von toolgeneriertem Code), ist es akzeptabel, den Partial Modifizierer der sichtbaren Deklaration zu verlassen, aber in den ausgeblendeten Deklarationen anzugeben.
Nur Klassen und Strukturen können mit teilweisen Deklarationen deklariert werden. Der Acker eines Typs wird beim Zusammengleichen von Teildeklarationen berücksichtigt: Zwei Klassen mit demselben Namen, aber unterschiedliche Zahlen von Typparametern werden nicht als partielle Deklarationen derselben Zeit betrachtet. Partielle Deklarationen können Attribute, Klassenmodifizierer, Inherits Anweisung oder Implements Anweisung angeben. Zur Kompilierungszeit werden alle Teile der Teildeklarationen kombiniert und als Teil der Typdeklaration verwendet. Wenn Konflikte zwischen Attributen, Modifizierern, Basen, Schnittstellen oder Typmember auftreten, führt ein Kompilierungszeitfehler zu einem Fehler. Beispiel:
Public Partial Class Test1
Implements IDisposable
End Class
Class Test1
Inherits Object
Implements IComparable
End Class
Public Partial Class Test2
End Class
Private Partial Class Test2
End Class
Im vorherigen Beispiel wird ein Typ Test1Publicdeklariert, der Object vom Typ erbt System.IDisposable und implementiert und implementiert.System.IComparable Die teilweisen Deklarationen Test2 führen zu einem Kompilierungszeitfehler, da eine der Deklarationen besagt, dass Test2 dies der Grund ist Public und ein anderer besagt, dass Test2 es sich um Privateeinen Fehler handelt.
Partielle Typen mit Typparametern können Einschränkungen und Varianzen für die Typparameter deklarieren, aber die Einschränkungen und Varianzen aus jeder partiellen Deklaration müssen übereinstimmen. Daher sind Einschränkungen und Varianzen besonders darauf spezialisiert, dass sie nicht automatisch wie andere Modifizierer kombiniert werden:
Partial Public Class List(Of T As IEnumerable)
End Class
' Error: Constraints on T don't match
Class List(Of T As IComparable)
End Class
Die Tatsache, dass ein Typ mithilfe mehrerer Partieller Deklarationen deklariert wird, wirkt sich nicht auf die Namensuchregeln innerhalb des Typs aus. Daher kann eine partielle Typdeklaration Member verwenden, die in anderen partiellen Typdeklarationen deklariert sind, oder Methoden für Schnittstellen implementieren, die in anderen Teiltypdeklarationen deklariert sind. Beispiel:
Public Partial Class Test1
Implements IDisposable
Private IsDisposed As Boolean = False
End Class
Class Test1
Private Sub Dispose() Implements IDisposable.Dispose
If Not IsDisposed Then
...
End If
End Sub
End Class
Geschachtelte Typen können auch partielle Deklarationen enthalten. Beispiel:
Public Partial Class Test
Public Partial Class NestedTest
Public Sub S1()
End Sub
End Class
End Class
Public Partial Class Test
Public Partial Class NestedTest
Public Sub S2()
End Sub
End Class
End Class
Initialisierer innerhalb einer Teildeklaration werden weiterhin in der Deklarationsreihenfolge ausgeführt; Es gibt jedoch keine garantierte Reihenfolge der Ausführung für Initialisierer, die in separaten Teildeklarationen auftreten.
Konstruierte Typen
Eine generische Typdeklaration selbst bezeichnet keinen Typ. Stattdessen kann eine generische Typdeklaration als "Blueprint" verwendet werden, um viele verschiedene Typen zu bilden, indem Typargumente angewendet werden. Ein generischer Typ, auf den Typargumente angewendet werden, wird als konstruierter Typ bezeichnet. Die Typargumente in einem konstruierten Typ müssen immer die Einschränkungen erfüllen, die für die Typparameter gelten, mit der sie übereinstimmen.
Ein Typname kann einen konstruierten Typ identifizieren, obwohl er keine Typparameter direkt angibt. Dies kann vorkommen, wenn ein Typ in einer generischen Klassendeklaration geschachtelt ist und der Instanztyp der enthaltenden Deklaration implizit für die Namenssuche verwendet wird:
Class Outer(Of T)
Public Class Inner
End Class
' Type of i is the constructed type Outer(Of T).Inner
Public i As Inner
End Class
Auf einen konstruierten Typ C(Of T1,...,Tn) kann zugegriffen werden, wenn auf den generischen Typ und alle Typargumente zugegriffen werden kann. Wenn beispielsweise der generische Typ C und Public alle Typargumente T1,...,Tn sind Public, lautet der konstruierte Typ Public. Wenn der Typname oder eines der Typargumente Privatejedoch lautet, lautet Privatedie Barrierefreiheit des konstruierten Typs . Wenn ein Typargument des konstruierten Typs und Protected ein anderes Typargument lautet Friend, kann auf den konstruierten Typ nur in der Klasse und den Unterklassen in dieser Assembly oder einer Assembly zugegriffen werden, die Zugriff gewährt Friend hat. Anders ausgedrückt: Die Barrierefreiheitsdomäne für einen konstruierten Typ ist die Schnittmenge der Barrierefreiheitsdomänen ihrer Bestandteile.
Hinweis: Die Tatsache, dass die Barrierefreiheitsdomäne des konstruierten Typs die Schnittmenge seiner konstituierten Teile ist, hat den interessanten Nebeneffekt, eine neue Barrierefreiheitsstufe zu definieren. Ein konstruierter Typ, der ein Element enthält, das und ein Element enthältProtected, auf das Friend nur in Kontexten zugegriffen werden kann, auf die sowohlalsProtectedauchFriend auf Member zugegriffen werden kann. Es gibt jedoch keine Möglichkeit, diese Barrierefreiheitsstufe in der Sprache auszudrücken, da die Barrierefreiheit Protected Friend bedeutet, dass auf eine Entität in einem Kontext zugegriffen werden kann, auf den entwederFriendoderProtected Mitglieder zugreifen können.
Die Basis-, implementierten Schnittstellen und Elemente von konstruierten Typen werden durch Ersetzen der angegebenen Typargumente für jedes Vorkommen des Typparameters im generischen Typ bestimmt.
Offene Typen und geschlossene Typen
Ein konstruierter Typ, für den ein oder mehrere Typargumente Typparameter eines enthaltenden Typs oder einer Methode sind, wird als offener Typ bezeichnet. Dies liegt daran, dass einige der Typparameter des Typs noch nicht bekannt sind, sodass die tatsächliche Form des Typs noch nicht vollständig bekannt ist. Im Gegensatz dazu wird ein generischer Typ, dessen Typargumente alle Parameter ohne Typ sind, als geschlossener Typ bezeichnet. Die Form eines geschlossenen Typs ist immer vollständig bekannt. Beispiel:
Class Base(Of T, V)
End Class
Class Derived(Of V)
Inherits Base(Of Integer, V)
End Class
Class MoreDerived
Inherits Derived(Of Double)
End Class
Der konstruierte Typ Base(Of Integer, V) ist ein offener Typ, da obwohl der Typparameter T angegeben wurde, der Typparameter U einen anderen Typparameter angegeben wurde. Somit ist die vollständige Form des Typs noch nicht bekannt. Der konstruierte Typ ist jedoch ein geschlossener Typ Derived(Of Double), da alle Typparameter in der Vererbungshierarchie bereitgestellt wurden.
Offene Typen sind wie folgt definiert:
Ein Typparameter ist ein offener Typ.
Ein Arraytyp ist ein offener Typ, wenn der Elementtyp ein offener Typ ist.
Ein konstruierter Typ ist ein offener Typ, wenn eines oder mehrere seiner Typargumente ein offener Typ sind.
Ein geschlossener Typ ist ein Typ, der kein offener Typ ist.
Da sich der Programmeinstiegspunkt nicht in einem generischen Typ befinden kann, werden alle zur Laufzeit verwendeten Typen geschlossen.
Sondertypen
.NET Framework enthält eine Reihe von Klassen, die speziell von .NET Framework und von der Visual Basic-Sprache behandelt werden:
Der Typ System.Void, der einen ungültigen Typ in .NET Framework darstellt, kann nur in GetType Ausdrücken direkt referenziert werden.
Die Typen System.RuntimeArgumentHandleSystem.ArgIterator und System.TypedReference alle können Zeiger in den Stapel enthalten und können daher nicht im .NET Framework-Heap angezeigt werden. Daher können sie nicht als Arrayelementtypen, Rückgabetypen, Feldtypen, generische Typargumente, nullfähige Typen, ByRef Parametertypen, den Typ eines zu konvertierenden Object Werts oder System.ValueType, das Ziel eines Aufrufs für Instanzen von Object oder System.ValueTypeoder oder aufgehoben in eine Schließung verwendet werden.
Visual Basic language spec