Udostępnij za pomocą


Konwersje w Visual Basic

Konwersja to proces zmiany wartości z jednego typu na inny. Na przykład wartość typu Integer można przekonwertować na wartość typu lub wartość typu Doublemożna przekonwertować na wartość typu BaseDerived , przy założeniu, że Base zarówno klasy, jak i DerivedDerived dziedziczą z Baseklasy . Konwersje mogą nie wymagać zmiany samej wartości (jak w ostatnim przykładzie) lub mogą wymagać znaczących zmian w reprezentacji wartości (jak w poprzednim przykładzie).

Konwersje mogą być rozszerzane lub zawężane. Konwersja rozszerzająca to konwersja z typu na inny typ, którego domena wartości jest co najmniej tak duża, jeśli nie większa, niż domena wartości oryginalnego typu. Konwersje rozszerzające nigdy nie powinny zakończyć się niepowodzeniem. Konwersja zawężania to konwersja z typu na inny typ, którego domena wartości jest mniejsza niż domena wartości oryginalnego typu lub wystarczająco niepowiązana, że podczas konwersji należy zachować dodatkową ostrożność (na przykład podczas konwersji z Integer na String). Zawężenie konwersji, co może wiązać się z utratą informacji, może zakończyć się niepowodzeniem.

Konwersja tożsamości (tj. konwersja typu do samego siebie) i konwersja wartości domyślnej (tj. konwersja z Nothing) jest definiowana dla wszystkich typów.

Niejawne i jawne konwersje

Konwersje mogą być niejawne lub jawne. Konwersje niejawne występują bez żadnej specjalnej składni. Poniżej przedstawiono przykład niejawnej Long konwersji Integer wartości na wartość:

Module Test
    Sub Main()
        Dim intValue As Integer = 123
        Dim longValue As Long = intValue

        Console.WriteLine(intValue & " = " & longValue)
    End Sub
End Module

Z drugiej strony jawne konwersje wymagają operatorów rzutów. Próba wykonania jawnej konwersji wartości bez operatora rzutowania powoduje błąd czasu kompilacji. W poniższym przykładzie użyto jawnej konwersji, aby przekonwertować Long wartość na Integer wartość.

Module Test
    Sub Main()
        Dim longValue As Long = 134
        Dim intValue As Integer = CInt(longValue)

        Console.WriteLine(longValue & " = " & intValue)
    End Sub
End Module

Zestaw niejawnych konwersji zależy od środowiska kompilacji i instrukcji Option Strict . Jeśli są używane ścisłe semantyka, tylko konwersje rozszerzające mogą wystąpić niejawnie. Jeśli są używane semantyka permisywna, wszystkie konwersje rozszerzające i zawężające (innymi słowy, wszystkie konwersje) mogą wystąpić niejawnie.

Konwersje logiczne

Chociaż Boolean nie jest typem liczbowym, ma konwersje zawężające do i z typów liczbowych tak, jakby były typem wyliczanym. Literał True konwertuje na literał 255Byte65535 dla , dla UShortUInteger429496729518446744073709551615 , dla , dla ULong, i na wyrażenie -1 dla SByte, , Short, IntegerDecimalLongSinglei .Double Literał False konwertuje na literał 0. Wartość liczbowa zero konwertuje na literał False. Wszystkie inne wartości liczbowe są konwertowane na literał True.

Istnieje konwersja zawężająca z wartości logicznej na ciąg, konwertując wartość na System.Boolean.TrueString wartość lub System.Boolean.FalseString. Istnieje również konwersja zawężająca z String do Boolean: jeśli ciąg był równy TrueString lub FalseString (w bieżącej kulturze, bez uwzględniania wielkości liter), następnie używa odpowiedniej wartości; w przeciwnym razie próbuje przeanalizować ciąg jako typ liczbowy (w szesnastku lub ósemkowym, jeśli to możliwe, jako zmiennoprzecinkowe) i używa powyższych reguł; w przeciwnym razie zgłasza System.InvalidCastExceptionwartość .

Konwersje liczbowe

Konwersje liczbowe istnieją między typami Byte, , UShortUIntegerIntegerShortSByteLongDecimalULongSingle i Doublewszystkimi typami wyliczonymi. Podczas konwertowania wyliczane typy są traktowane tak, jakby były ich typami bazowymi. Podczas konwertowania na typ wyliczony wartość źródłowa nie jest wymagana do zachowania zgodności z zestawem wartości zdefiniowanych w typie wyliczanym. Przykład:

Enum Values
    One
    Two
    Three
End Enum

Module Test
    Sub Main()
        Dim x As Integer = 5

        ' OK, even though there is no enumerated value for 5.
        Dim y As Values = CType(x, Values)
    End Sub
End Module

Konwersje liczbowe są przetwarzane w czasie wykonywania w następujący sposób:

  • W przypadku konwersji z typu liczbowego na szerszy typ liczbowy wartość jest po prostu konwertowana na szerszy typ. Konwersje z UInteger, , ULongInteger, Longlub Decimal do Single lub Double są zaokrąglane do najbliższej Single lub Double wartości. Chociaż ta konwersja może spowodować utratę dokładności, nigdy nie spowoduje utraty wielkości.

  • W przypadku konwersji z typu całkowitego do innego typu całkowitego lub od Singletypu , Doublelub Decimal do typu całkowitego wynik zależy od tego, czy sprawdzanie przepełnienia liczby całkowitej jest następujące:

    Jeśli przepełnienie całkowite jest sprawdzane:

    • Jeśli źródło jest typem całkowitym, konwersja powiedzie się, jeśli argument źródłowy mieści się w zakresie typu docelowego. Konwersja zgłasza System.OverflowException wyjątek, jeśli argument źródłowy znajduje się poza zakresem typu docelowego.

    • Jeśli źródło ma Singlewartość , Doublelub Decimal, wartość źródłowa jest zaokrąglona w górę lub w dół do najbliższej wartości całkowitej, a ta wartość całkowita staje się wynikiem konwersji. Jeśli wartość źródłowa jest równie zbliżona do dwóch wartości całkowitych, wartość jest zaokrąglona do wartości, która ma parzystą liczbę w najmniej znaczącej pozycji cyfrowej. Jeśli wynikowa wartość całkowita znajduje się poza zakresem typu docelowego, zgłaszany System.OverflowException jest wyjątek.

    Jeśli przepełnienie liczby całkowitej nie jest sprawdzane:

    • Jeśli źródło jest typem całkowitym, konwersja zawsze się powiedzie i po prostu składa się z odrzucania najbardziej znaczących bitów wartości źródłowej.

    • Jeśli źródłem jest Single, Doublelub Decimal, konwersja zawsze się powiedzie i po prostu składa się z zaokrąglania wartości źródłowej do najbliższej wartości całkowitej. Jeśli wartość źródłowa jest równie zbliżona do dwóch wartości całkowitych, wartość jest zawsze zaokrąglona do wartości, która ma liczbę parzystą w najmniej znaczącej pozycji cyfry.

  • W przypadku konwersji z Double na Singlewartość Double wartość jest zaokrąglona do najbliższej Single wartości. Double Jeśli wartość jest za mała, aby reprezentować jako Singlewartość , wynik staje się dodatni zero lub zero ujemne. Double Jeśli wartość jest zbyt duża, aby reprezentować jako Singlewartość , wynik staje się dodatnią nieskończonością lub nieskończonością ujemną. Double Jeśli wartość to NaN, wynik to również NaN.

  • W przypadku konwersji z Single lub Double na Decimalwartość wartość źródłowa jest konwertowana na Decimal reprezentację i zaokrąglana do najbliższej liczby po 28 miejscu dziesiętnym, jeśli jest to wymagane. Jeśli wartość źródłowa jest za mała, aby reprezentować jako Decimalwartość , wynik stanie się zerowy. Jeśli wartość źródłowa to NaN, nieskończoność lub zbyt duża, aby reprezentować jako Decimal, System.OverflowException zgłaszany jest wyjątek.

  • W przypadku konwersji z Double na Singlewartość Double wartość jest zaokrąglona do najbliższej Single wartości. Double Jeśli wartość jest za mała, aby reprezentować jako Singlewartość , wynik staje się dodatni zero lub zero ujemne. Double Jeśli wartość jest zbyt duża, aby reprezentować jako Singlewartość , wynik staje się dodatnią nieskończonością lub nieskończonością ujemną. Double Jeśli wartość to NaN, wynik to również NaN.

Konwersje odwołań

Typy odwołań mogą być konwertowane na typ podstawowy i odwrotnie. Konwersje z typu podstawowego na bardziej pochodny typ kończą się powodzeniem tylko w czasie wykonywania, jeśli przekonwertowana wartość jest wartością null, samym typem pochodnym lub bardziej pochodnym typem.

Typy klas i interfejsów można rzutować do i z dowolnego typu interfejsu. Konwersje między typem a typem interfejsu kończą się powodzeniem tylko w czasie wykonywania, jeśli rzeczywiste typy, których dotyczy, mają relację dziedziczenia lub implementacji. Ponieważ typ interfejsu zawsze będzie zawierać wystąpienie typu pochodzącego z Objectklasy , typ interfejsu może być również zawsze rzutowy do i z Objectklasy .

Uwaga. Nie jest to błąd podczas konwertowania NotInheritable klas na i z interfejsów, które nie są implementowane, ponieważ klasy reprezentujące klasy COM mogą mieć implementacje interfejsu, które nie są znane do czasu wykonywania.

Jeśli konwersja odwołania zakończy się niepowodzeniem w czasie wykonywania, System.InvalidCastException zostanie zgłoszony wyjątek.

Konwersje wariancji odwołań

Interfejsy ogólne lub delegaty mogą mieć parametry typu wariantu, które umożliwiają konwersje między zgodnymi wariantami typu. W związku z tym w czasie wykonywania konwersja z typu klasy lub typu interfejsu na typ interfejsu zgodny z typem interfejsu dziedziczy lub implementuje powodzenie. Podobnie typy delegatów można rzutować do i z typów delegatów zgodnych z wariantami. Na przykład typ delegata

Delegate Function F(Of In A, Out R)(a As A) As R

umożliwia konwersję z F(Of Object, Integer) na F(Of String, Integer). Oznacza to, że delegat F , który przyjmuje Object , może być bezpiecznie używany jako delegat F , który przyjmuje String. Po wywołaniu delegata metoda docelowa będzie oczekiwać obiektu, a ciąg jest obiektem.

Ogólny delegat lub typ S(Of S1,...,Sn) interfejsu jest mówi się, że wariant jest zgodny z interfejsem ogólnym lub typem T(Of T1,...,Tn) delegata, jeśli:

  • S i T są konstruowane z tego samego typu U(Of U1,...,Un)ogólnego .

  • Dla każdego parametru Uxtypu :

    • Jeśli parametr typu został zadeklarowany bez wariancji, Sx to i Tx musi być tego samego typu.

    • Jeśli parametr typu został zadeklarowany In , musi istnieć tożsamość rozszerzająca, domyślna, odwołanie, tablica lub konwersja parametru typu z Sx na Tx.

    • Jeśli parametr typu został zadeklarowany Out , musi istnieć tożsamość rozszerzająca, domyślna, odwołanie, tablica lub konwersja parametru typu z Tx na Sx.

W przypadku konwertowania z klasy na interfejs ogólny z parametrami typu wariantu, jeśli klasa implementuje więcej niż jeden interfejs zgodny z wariantem, konwersja jest niejednoznaczna, jeśli nie ma konwersji innej niż wariant. Przykład:

Class Base
End Class

Class Derived1
    Inherits Base
End Class

Class Derived2
    Inherits Base
End Class

Class OneAndTwo
    Implements IEnumerable(Of Derived1)
    Implements IEnumerable(Of Derived2)
End Class

Class BaseAndOneAndTwo
    Implements IEnumerable(Of Base)
    Implements IEnumerable(Of Derived1)
    Implements IEnumerable(Of Derived2)
End Class

Module Test
    Sub Main()
        ' Error: conversion is ambiguous
        Dim x As IEnumerable(Of Base) = New OneAndTwo()

        ' OK, will pick up the direct implementation of IEnumerable(Of Base)
        Dim y as IEnumerable(Of Base) = New BaseAndOneAndTwo()
    End Sub
End Module

Anonimowe konwersje delegatów

Gdy wyrażenie sklasyfikowane jako metoda lambda jest ponownie sklasyfikowane jako wartość w kontekście, w którym nie ma typu docelowego (na przykład Dim x = Function(a As Integer, b As Integer) a + b), lub gdy typ docelowy nie jest typem delegata, typ wyrażenia wynikowego jest anonimowym typem delegata równoważnym podpisowi metody lambda. Ten anonimowy typ delegata ma konwersję na dowolny zgodny typ delegata: zgodny typ delegata to dowolny typ delegata, który można utworzyć przy użyciu wyrażenia tworzenia delegata z metodą anonimowego typu delegata Invoke jako parametr. Przykład:

' Anonymous delegate type similar to Func(Of Object, Object, Object)
Dim x = Function(x, y) x + y

' OK because delegate type is compatible
Dim y As Func(Of Integer, Integer, Integer) = x

Należy pamiętać, że typy System.Delegate i System.MulticastDelegate nie są traktowane jako typy delegatów (mimo że wszystkie typy delegatów dziedziczą po nich). Należy również pamiętać, że konwersja typu anonimowego delegata na zgodny typ delegata nie jest konwersją referencyjną.

Konwersje tablic

Oprócz konwersji zdefiniowanych na tablicach ze względu na fakt, że są typami referencyjnymi, istnieje kilka specjalnych konwersji dla tablic.

W przypadku dwóch typów A i , jeśli są to zarówno typy referencyjne, jak i Bparametry typu, które nie są znane jako typy wartości, a jeśli A ma konwersję odwołania, tablicy lub parametru typu do B, konwersja istnieje z tablicy typu A do tablicy typu B o tej samej rangi. Ta relacja jest nazywana kowariancją tablicy. Wariancja tablicy w szczególności oznacza, że element tablicy, którego typ elementu jest rzeczywiście elementem tablicy, którego typem elementu jest BA, pod warunkiem, że oba A typy i B są typami referencyjnymi i które B mają konwersję odwołania lub konwersję tablicy na A. W poniższym przykładzie drugie wywołanie powoduje zgłoszenie wyjątkuF, ponieważ rzeczywisty typ b elementu to String, a nie Object:System.ArrayTypeMismatchException

Module Test
    Sub F(ByRef x As Object)
    End Sub

    Sub Main()
        Dim a(10) As Object
        Dim b() As Object = New String(10) {}
        F(a(0)) ' OK.
        F(b(1)) ' Not allowed: System.ArrayTypeMismatchException.
   End Sub
End Module

Ze względu na kowariancję tablic, przypisania do elementów tablic typów odwołań obejmują sprawdzanie czasu wykonywania, które gwarantuje, że wartość przypisana do elementu tablicy jest rzeczywiście dozwolonym typem.

Module Test
    Sub Fill(array() As Object, index As Integer, count As Integer, _
            value As Object)
        Dim i As Integer

        For i = index To (index + count) - 1
            array(i) = value
        Next i
    End Sub

    Sub Main()
        Dim strings(100) As String

        Fill(strings, 0, 101, "Undefined")
        Fill(strings, 0, 10, Nothing)
        Fill(strings, 91, 10, 0)
    End Sub
End Module

W tym przykładzie przypisanie metody array(i) w metodzie Fill niejawnie zawiera sprawdzanie czasu wykonywania, które gwarantuje, że obiekt, do którego odwołuje się zmienna value , jest albo Nothing wystąpieniem typu zgodnego z rzeczywistym typem elementu tablicy array. W metodzie Mainpierwsze dwa wywołania metody Fill kończą się powodzeniem, ale trzecie wywołanie powoduje System.ArrayTypeMismatchException zgłoszenie wyjątku podczas wykonywania pierwszego przypisania do array(i)metody . Wyjątek występuje, ponieważ Integer nie można go przechowywać w tablicy String .

Jeśli jednym z typów elementów tablicy jest parametr typu, którego typ okazuje się być typem wartości w czasie wykonywania, System.InvalidCastException zostanie zgłoszony wyjątek. Przykład:

Module Test
    Sub F(Of T As U, U)(x() As T)
        Dim y() As U = x
    End Sub

    Sub Main()
        ' F will throw an exception because Integer() cannot be
        ' converted to Object()
        F(New Integer() { 1, 2, 3 })
    End Sub
End Module

Konwersje istnieją również między tablicą typu wyliczonego a tablicą typu wyliczonego typu bazowego lub tablicą innego typu wyliczanego z tym samym typem bazowym, pod warunkiem, że tablice mają tę samą rangę.

Enum Color As Byte
    Red
    Green
    Blue
End Enum

Module Test
    Sub Main()
        Dim a(10) As Color
        Dim b() As Integer
        Dim c() As Byte

        b = a    ' Error: Integer is not the underlying type of Color
        c = a    ' OK
        a = c    ' OK
    End Sub
End Module

W tym przykładzie tablica jest konwertowana na i z tablicy Color typu bazowego Byte. Color Konwersja na tablicę Integerklasy będzie jednak błędem, ponieważ Integer nie jest bazowym typem Color.

Tablica rank-1 typu A() ma również konwersję tablicy na typy interfejsów IList(Of B)kolekcji , IReadOnlyList(Of B), ICollection(Of B)IReadOnlyCollection(Of B) i IEnumerable(Of B) znalezionych w System.Collections.Genericpliku , o ile jedna z następujących wartości ma wartość true:

  • A i są zarówno typami referencyjnymi, jak i B parametrami typu, które nie są znane jako typy wartości; i A mają rozszerzające odwołanie, tablicę lub konwersję parametru typu na B; lub
  • A i B są wyliczane typy tego samego typu bazowego; lub
  • jeden z A elementów i B jest typem wyliczanym, a drugi jest jego typem bazowym.

Każda tablica typu A z dowolną rangą ma również konwersję tablicy na typy interfejsów IListkolekcji nieogólne i ICollectionIEnumerable znalezione w pliku System.Collections.

Możliwe jest iterowanie interfejsów wynikowych przy użyciu metody For Eachlub bezpośrednie wywołanie GetEnumerator metod. W przypadku tablic rank-1 przekonwertowanych ogólnych lub niegenerycznych form IList lub ICollection, można również uzyskać elementy według indeksu. W przypadku tablic rank-1 przekonwertowanych na ogólne lub nieogólne formy IListklasy można również ustawić elementy według indeksu, z zastrzeżeniem tych samych testów współwariancji tablicy środowiska uruchomieniowego, jak opisano powyżej. Zachowanie wszystkich innych metod interfejsu jest niezdefiniowane przez specyfikację języka VB; jest on do bazowego środowiska uruchomieniowego.

Konwersje typów wartości

Wartość typu wartości można przekonwertować na jeden z podstawowych typów odwołań lub typ interfejsu implementowany przez proces nazywany boxingiem. Gdy wartość typu wartości jest w polu, wartość jest kopiowana z lokalizacji, w której znajduje się na stercie programu .NET Framework. Następnie zwracane jest odwołanie do tej lokalizacji na stercie i może być przechowywane w zmiennej typu odwołania. To odwołanie jest również określane jako wystąpienie pola typu wartości. Wystąpienie w polu ma taką samą semantykę jak typ odwołania zamiast typu wartości.

Typy wartości w polu można przekonwertować z powrotem na ich oryginalny typ wartości za pomocą procesu nazywanego rozpboxowaniem. Gdy pole typu wartości jest rozpalane, wartość jest kopiowana ze sterta do lokalizacji zmiennej. Od tego momentu zachowuje się tak, jakby był to typ wartości. W przypadku rozpakowania typu wartości wartość musi być wartością null lub wystąpieniem typu wartości. W przeciwnym razie zgłaszany System.InvalidCastException jest wyjątek. Jeśli wartość jest wystąpieniem typu wyliczonego, ta wartość może być również rozpakowane do typu bazowego typu wyliczanego lub innego typu wyliczanego, który ma ten sam typ bazowy. Wartość null jest traktowana tak, jakby była literałem Nothing.

Aby dobrze obsługiwać typy wartości dopuszczających wartość null, typ System.Nullable(Of T) wartości jest traktowany specjalnie podczas boksowania i rozpakowania. Boxing wartość typu Nullable(Of T) powoduje pole wartości typu T , jeśli właściwość wartości HasValue jest True lub wartość Nothing , jeśli właściwość wartości HasValue jest False. Rozpakowywanie wartości typu TNullable(Of T) powoduje wystąpienie, Nullable(Of T) którego Value właściwość jest wartością w polu i której HasValue właściwością jest True. Wartość Nothing można usunąć z pola wyboru Nullable(Of T) dla dowolnego T elementu i powoduje wyświetlenie wartości, której HasValue właściwość to False. Ponieważ typy wartości w polu zachowują się jak typy odwołań, istnieje możliwość utworzenia wielu odwołań do tej samej wartości. W przypadku typów pierwotnych i wyliczonych typów jest to nieistotne, ponieważ wystąpienia tych typów są niezmienne. Oznacza to, że nie można zmodyfikować boxed wystąpienia tych typów, więc nie można obserwować faktu, że istnieje wiele odwołań do tej samej wartości.

Struktury, z drugiej strony, mogą być modyfikowalne, jeśli jego zmienne wystąpienia są dostępne lub jeśli jego metody lub właściwości modyfikują zmienne wystąpienia. Jeśli jedno odwołanie do struktury skrzynkowej jest używane do modyfikowania struktury, wszystkie odwołania do struktury skrzynkowej będą widzieć zmianę. Ponieważ ten wynik może być nieoczekiwany, gdy wartość typowana jako Object jest kopiowana z jednej lokalizacji do innego typu wartości w polu, zostanie automatycznie sklonowana na stercie zamiast jedynie skopiować odwołania. Przykład:

Class Class1
    Public Value As Integer = 0
End Class

Structure Struct1
    Public Value As Integer
End Structure

Module Test
    Sub Main()
        Dim val1 As Object = New Struct1()
        Dim val2 As Object = val1

        val2.Value = 123

        Dim ref1 As Object = New Class1()
        Dim ref2 As Object = ref1

        ref2.Value = 123

        Console.WriteLine("Values: " & val1.Value & ", " & val2.Value)
        Console.WriteLine("Refs: " & ref1.Value & ", " & ref2.Value)
    End Sub
End Module

Dane wyjściowe programu to:

Values: 0, 123
Refs: 123, 123

Przypisanie do pola zmiennej val2 lokalnej nie ma wpływu na pole zmiennej val1 lokalnej, ponieważ gdy pole zostało Struct1 przypisane do val2, kopia wartości została wykonana. Z kolei przypisanie ref2.Value = 123 ma wpływ na obiekt, który zarówno ref1 , jak i ref2 odwołuje się.

Uwaga. Kopiowanie struktury nie jest wykonywane w przypadku struktur w polu typowanych, ponieważ System.ValueType nie można opóźnić powiązania elementu System.ValueType.

Istnieje jeden wyjątek od reguły, która typy wartości w polu zostaną skopiowane podczas przypisywania. Jeśli odwołanie typu wartości pola jest przechowywane w innym typie, odwołanie wewnętrzne nie zostanie skopiowane. Przykład:

Structure Struct1
    Public Value As Object
End Structure

Module Test
    Sub Main()
        Dim val1 As Struct1
        Dim val2 As Struct1

        val1.Value = New Struct1()
        val1.Value.Value = 10

        val2 = val1
        val2.Value.Value = 123
        Console.WriteLine("Values: " & val1.Value.Value & ", " & _
            val2.Value.Value)
    End Sub
End Module

Dane wyjściowe programu to:

Values: 123, 123

Dzieje się tak, ponieważ wartość wewnętrzna nie jest kopiowana podczas kopiowania wartości. W związku z tym zarówno, jak val1.Value i val2.Value mają odwołanie do tego samego typu wartości pola.

Uwaga. Fakt, że typy wartości w polu wewnętrznym nie są kopiowane, jest ograniczeniem systemu typów platformy .NET — aby upewnić się, że wszystkie typy wartości wewnętrznych zostały skopiowane za każdym razem, gdy skopiowana wartość typu Object byłaby zbyt kosztowna.

Jak opisano wcześniej, typy wartości w polu mogą być rozdzielane tylko do ich oryginalnego typu. Typy pierwotne boxed są jednak traktowane specjalnie w przypadku wpisywania jako Object. Można je przekonwertować na dowolny inny typ pierwotny, do którego mają konwersję. Przykład:

Module Test
    Sub Main()
        Dim o As Object = 5
        Dim b As Byte = CByte(o)  ' Legal
        Console.WriteLine(b) ' Prints 5
    End Sub
End Module

Zwykle nie można rozpiąć wartości 5 pola Integer do zmiennejByte. Jednak ponieważ Integer i Byte są typami pierwotnymi i mają konwersję, konwersja jest dozwolona.

Należy pamiętać, że konwertowanie typu wartości na interfejs różni się od ogólnego argumentu ograniczonego do interfejsu. Podczas uzyskiwania dostępu do elementów członkowskich interfejsu przy użyciu ograniczonego parametru typu (lub wywoływania metod w systemie Object) boxing nie występuje tak samo, jak w przypadku konwersji typu wartości na interfejs, a dostęp do elementu członkowskiego interfejsu jest uzyskiwany. Załóżmy na przykład, że interfejs ICounter zawiera metodę Increment , która może służyć do modyfikowania wartości. Jeśli ICounter jest używany jako ograniczenie, implementacja Increment metody jest wywoływana z odwołaniem do zmiennej, która Increment została wywołana, a nie kopii pola:

Interface ICounter
    Sub Increment()
    ReadOnly Property Value() As Integer
End Interface

Structure Counter
    Implements ICounter

    Dim _value As Integer

    Property Value() As Integer Implements ICounter.Value
        Get
            Return _value
        End Get
    End Property

    Sub Increment() Implements ICounter.Increment
       value += 1
    End Sub
End Structure

Module Test
      Sub Test(Of T As ICounter)(x As T)
         Console.WriteLine(x.value)
         x.Increment()                     ' Modify x
         Console.WriteLine(x.value)
         CType(x, ICounter).Increment()    ' Modify boxed copy of x
         Console.WriteLine(x.value)
      End Sub

      Sub Main()
         Dim x As Counter
         Test(x)
      End Sub
End Module

Pierwsze wywołanie Increment modyfikuje wartość w zmiennej x. Nie jest to równoważne drugiemu wywołaniu funkcji Increment, które modyfikuje wartość w zamkniętej kopii x. W związku z tym dane wyjściowe programu to:

0
1
1

Konwersje typów wartości dopuszczanych do wartości null

Typ T wartości może konwertować na i z wersji dopuszczanej do wartości null typu . T? Konwersja z T? na T zgłasza System.InvalidOperationException wyjątek, jeśli przekonwertowana wartość to Nothing. Ponadto ma konwersję na typS, T? jeśli T ma wewnętrzną konwersję na S. A jeśli S jest typem wartości, między i istnieją następujące konwersje wewnętrzne między T? i S?:

  • Konwersja tej samej klasyfikacji (zawężanie lub rozszerzanie) z T? na S?.

  • Konwersja tej samej klasyfikacji (zawężanie lub rozszerzanie) z T na S?.

  • Konwersja zawężająca z S? do T.

Na przykład istnieje wewnętrzna konwersja rozszerzająca od Integer? do Long? , ponieważ istnieje wewnętrzna konwersja rozszerzająca z Integer na Long:

Dim i As Integer? = 10
Dim l As Long? = i

Podczas konwertowania wartości z T? na S?wartość , jeśli wartość T? to Nothing, wartość parametru S? to Nothing. W przypadku konwersji z S? na lub T? na TS, jeśli wartość lub S? ma Nothingwartość T? , System.InvalidCastException zostanie zgłoszony wyjątek.

Ze względu na zachowanie typu System.Nullable(Of T)bazowego , gdy typ T? wartości dopuszczanej do wartości null jest w polu, wynik jest wartością pola typu T, a nie wartością pola typu T?. I, z drugiej strony, podczas rozpakowania do typu T?wartości dopuszczającej wartość null, wartość zostanie opakowana przez System.Nullable(Of T), i Nothing zostanie rozpakowana do wartości null typu T?. Przykład:

Dim i1? As Integer = Nothing
Dim o1 As Object = i1

Console.WriteLine(o1 Is Nothing)                    ' Will print True
o1 = 10
i1 = CType(o1, Integer?)
Console.WriteLine(i1)                               ' Will print 10

Efektem ubocznym tego zachowania jest to, że typ T? wartości dopuszczającej wartość null wydaje się implementować wszystkie interfejsy Tprogramu , ponieważ konwertowanie typu wartości na interfejs wymaga pola typu . W rezultacie T? jest konwertowany na wszystkie interfejsy, które T są konwertowane na. Należy jednak pamiętać, że typ T? wartości dopuszczający wartość null nie implementuje interfejsów T dla celów ogólnego sprawdzania ograniczeń lub odbicia. Przykład:

Interface I1
End Interface

Structure T1
    Implements I1
    ...
End Structure

Module Test
    Sub M1(Of T As I1)(ByVal x As T)
    End Sub

    Sub Main()
        Dim x? As T1 = Nothing
        Dim y As I1 = x                ' Valid
        M1(x)                          ' Error: x? does not satisfy I1 constraint
    End Sub
End Module

Konwersje ciągów

Konwertowanie Char na String wyniki w ciągu, którego pierwszy znak jest wartością znaku. Konwertowanie String na Char wyniki w postaci, której wartość jest pierwszym znakiem ciągu. Konwertowanie tablicy Char na String wyniki w ciągu, którego znaki są elementami tablicy. Konwertowanie String na tablicę wyników w tablicy Char znaków, których elementy są znakami ciągu.

Dokładne konwersje między String i Boolean, Byte, UShortDecimalSingleDoubleSByteUIntegerDateShortIntegerULongLongi odwrotnie, wykraczają poza zakres tej specyfikacji i są zależne od implementacji z wyjątkiem jednego szczegółów. Konwersje ciągów zawsze uwzględniają bieżącą kulturę środowiska uruchomieniowego. W związku z tym muszą być wykonywane w czasie wykonywania.

Rozszerzanie konwersji

Konwersje rozszerzające nigdy nie przepełniają się, ale mogą wiązać się ze stratą precyzji. Następujące konwersje rozszerzają konwersje:

Konwersje tożsamości/domyślne

  • Od typu do samego siebie.

  • Na podstawie anonimowego typu delegata wygenerowanego dla metody lambda przeklasyfikowanie do dowolnego typu delegata z identycznym podpisem.

  • Od literału Nothing do typu.

Konwersje liczbowe

  • Z Byte do UShort, , ShortUIntegerIntegerULongLongDecimalSinglelub .Double

  • Od SByte do Short, Integer, Long, Decimal, Singlelub Double.

  • Od UShort do UInteger, Integer, ULong, Long, Decimal, Singlelub Double.

  • Z Short do Integer, Long, DecimalSingle lub Double.

  • Od UInteger do ULong, Long, Decimal, Singlelub Double.

  • Z Integer do Long, DecimalSingle lub Double.

  • Z ULong do Decimal, Singlelub Double.

  • Z Long do Decimal, Single lub Double.

  • Z Decimal do Single lub Double.

  • Od Single do Double.

  • Od literału 0 do wyliczonego typu. (Uwaga. Konwersja z 0 na dowolny typ wyliczony rozszerza się, aby uprościć flagi testowania. Jeśli na przykład Values jest typem wyliczonym z wartością One, możesz przetestować zmienną v typu Values , mówiąc (v And Values.One) = 0.

  • Od wyliczonego typu do bazowego typu liczbowego lub do typu liczbowego, do którego jego podstawowy typ liczbowy ma konwersję rozszerzającą.

  • Z wyrażenia stałego typu ULong, , LongUShortIntegerShortUIntegerBytelub SByte do węższego typu, pod warunkiem, że wartość wyrażenia stałego mieści się w zakresie typu docelowego. (Uwaga. Konwersje z UInteger lub do Single, ULongInteger lub LongSingleDoubledo lub DecimalSingleDouble lub mogą spowodować utratę precyzji, ale nigdy nie spowoduje utraty wielkości. Inne rozszerzające konwersje liczbowe nigdy nie tracą żadnych informacji).

Konwersje odwołań

  • Z typu odwołania do typu podstawowego.

  • Z typu odwołania do typu interfejsu, pod warunkiem, że typ implementuje interfejs lub interfejs zgodny z wariantem.

  • Z typu interfejsu do Object.

  • Od typu interfejsu do wariantu zgodnego typu interfejsu.

  • Z typu delegata do wariantu zgodnego typu delegata. (Uwaga. Wiele innych konwersji odwołań jest implikowane przez te reguły. Na przykład anonimowe delegaty to typy referencyjne dziedziczone z System.MulticastDelegate; typy tablic to typy referencyjne dziedziczone z System.Array; typy anonimowe to typy referencyjne dziedziczone z System.Object.)

Konwersje delegatów anonimowych

  • Na podstawie anonimowego typu delegata wygenerowanego dla metody lambda przeklasyfikowanie do dowolnego szerszego typu delegata.

Konwersje tablic

  • Z typu S tablicy z typem Se elementu do typu tablicy z typem TTeelementu , pod warunkiem, że wszystkie następujące elementy są prawdziwe:

    • S i T różnią się tylko w typie elementu.

    • Oba Se typy Te i są typami referencyjnymi lub są parametrami typu znanymi jako typ odwołania.

    • Rozszerzenie odwołania, tablicy lub konwersji parametru typu istnieje z Se do Te.

  • Z typu S tablicy z wyliczonym typem Se elementu do typu tablicy z typem TTeelementu , pod warunkiem, że wszystkie następujące elementy są prawdziwe:

    • S i T różnią się tylko w typie elementu.

    • Te jest podstawowym typem Se.

  • Z typu S tablicy rangi 1 z wyliczonym typem Seelementu , do System.Collections.Generic.IList(Of Te), IReadOnlyList(Of Te), ICollection(Of Te), IReadOnlyCollection(Of Te)i IEnumerable(Of Te), pod warunkiem, że jedna z następujących wartości ma wartość true:

    • Oba Se typy Te i są typami referencyjnymi lub są parametrami typu znanymi jako typ odwołania, a rozszerzenie odwołania, tablicy lub konwersji parametrów typu istnieje z Se do Te; lub

    • Te jest podstawowym typem Se; lub

    • Te jest identyczny z Se

Konwersje typu wartości

  • Od typu wartości do typu podstawowego.

  • Od typu wartości do typu interfejsu implementuje typ.

Konwersje typu wartości dopuszczanej do wartości null

  • Od typu T do typu T?.

  • Od typu T? do typu S?, gdzie istnieje konwersja rozszerzająca z typu T na typ S.

  • Od typu T do typu S?, gdzie istnieje konwersja rozszerzająca z typu T na typ S.

  • Od typu do typu T? interfejsu, który implementuje typ T .

Konwersje ciągów

  • Od Char do String.

  • Od Char() do String.

Konwersje parametrów typu

  • Z parametru typu do Object.

  • Od parametru typu do ograniczenia typu interfejsu lub dowolnego wariantu interfejsu zgodnego z ograniczeniem typu interfejsu.

  • Z parametru typu do interfejsu zaimplementowanego przez ograniczenie klasy.

  • Z parametru typu do wariantu interfejsu zgodnego z interfejsem zaimplementowanym przez ograniczenie klasy.

  • Z parametru typu do ograniczenia klasy lub podstawowego typu ograniczenia klasy.

  • Od parametru T typu do ograniczenia Txparametru typu lub dowolny Tx element ma konwersję rozszerzającą na.

Zawężanie konwersji

Konwersje zawężające to konwersje, których nie można udowodnić, że zawsze się powiedzie, konwersje, które są znane z utraty informacji, i konwersje między domenami typów wystarczająco różnią się, aby zawęzić notację. Następujące konwersje są klasyfikowane jako konwersje zawężające:

Konwersje logiczne

  • Z Boolean do Byte, , SByteUShortShortUIntegerIntegerULongLongDecimalSinglelub .Double

  • Z Byte, , UShortUIntegerLongIntegerShortSByteDecimalSingleULonglub Double do .Boolean

Konwersje liczbowe

  • Od Byte do SByte.

  • Z SByte do Byte, UShort, UIntegerlub ULong.

  • Z UShort do Byte, SBytelub Short.

  • Od Short do Byte, SByte, UShort, UIntegerlub ULong.

  • Od UInteger do Byte, SByte, UShort, Shortlub Integer.

  • Od Integer do Byte, SByte, UShort, Short, UIntegerlub ULong.

  • Od ULong do Byte, SByte, UShort, Short, UInteger, Integerlub Long.

  • Od Long do Byte, SByte, UShort, Short, UInteger, Integerlub ULong.

  • Z Decimal do Byte, SByte, UShort, Short, UInteger, Integer, ULonglub Long.

  • Z Single do Byte, , SByteUShortShortUIntegerIntegerULongLonglub .Decimal

  • Z Double do Byte, SByte, UShortShortUIntegerIntegerULongLong, , Decimallub .Single

  • Od typu liczbowego do wyliczonego typu.

  • Od wyliczonego typu do typu liczbowego jego podstawowy typ liczbowy ma konwersję zawężającą do.

  • Z wyliczonego typu do innego wyliczonego typu.

Konwersje odwołań

  • Od typu odwołania do bardziej pochodnego typu.

  • Z typu klasy do typu interfejsu, pod warunkiem, że typ klasy nie implementuje typu interfejsu ani wariantu typu interfejsu zgodnego z nim.

  • Od typu interfejsu do typu klasy.

  • Z typu interfejsu do innego typu interfejsu, pod warunkiem, że nie ma relacji dziedziczenia między dwoma typami i pod warunkiem, że nie są one zgodne z wariantem.

Konwersje delegatów anonimowych

  • Na podstawie anonimowego typu delegata wygenerowanego dla metody lambda przeklasyfikowanie do dowolnego węższego typu delegata.

Konwersje tablic

  • Z typu S tablicy z typem Seelementu do typu tablicy z typem TTeelementu , pod warunkiem, że wszystkie następujące elementy są prawdziwe:

    • S i T różnią się tylko w typie elementu.
    • Oba Se typy Te i są typami referencyjnymi lub są parametrami typu, które nie są znane jako typy wartości.
    • Konwersja parametrów odwołania, tablicy lub typu istnieje od Se do Te.
  • Z typu S tablicy z typem Se elementu do typu T tablicy z wyliczonym typem Teelementu , pod warunkiem, że wszystkie następujące elementy są prawdziwe:

    • S i T różnią się tylko w typie elementu.
    • Se jest podstawowym typem Te , lub są to oba różne typy wyliczane, które współużytkują ten sam typ bazowy.
  • Z typu S tablicy rangi 1 z wyliczonym typem Seelementu , do IList(Of Te), ICollection(Of Te)IReadOnlyList(Of Te)i IReadOnlyCollection(Of Te)IEnumerable(Of Te), pod warunkiem, że jedna z następujących wartości ma wartość true:

    • Oba Se typy Te i są typami referencyjnymi lub są parametrami typu znanymi jako typ odwołania, a zawężenie odwołania, tablicy lub konwersji parametru typu istnieje z Se do Te; lub
    • Se jest podstawowym typem Te, lub są to oba różne typy wyliczane, które współużytkują ten sam typ bazowy.

Konwersje typu wartości

  • Z typu odwołania do bardziej pochodnego typu wartości.

  • Z typu interfejsu do typu wartości, pod warunkiem, że typ wartości implementuje typ interfejsu.

Konwersje typu wartości dopuszczanej do wartości null

  • Od typu T? do typu T.

  • Od typu T? do typu S?, gdzie istnieje konwersja zawężająca z typu T na typ S.

  • Od typu T do typu S?, gdzie istnieje konwersja zawężająca z typu T na typ S.

  • Od typu S? do typu T, gdzie istnieje konwersja z typu S na typ T.

Konwersje ciągów

  • Od String do Char.

  • Od String do Char().

  • Od String do Boolean i od Boolean do String.

  • Konwersje między String i Byte, SByte, UShortULongUIntegerShortLongDecimalIntegerSinglelub .Double

  • Od String do Date i od Date do String.

Konwersje parametrów typu

  • Od Object do parametru typu.

  • Z parametru typu do typu interfejsu, pod warunkiem, że parametr typu nie jest ograniczony do tego interfejsu lub ograniczone do klasy, która implementuje ten interfejs.

  • Z typu interfejsu do parametru typu.

  • Z parametru typu do pochodnego typu ograniczenia klasy.

  • Od parametru T typu do dowolnego ograniczenia Tx parametru typu ma konwersję zawężającą do.

Konwersje parametrów typu

Konwersje parametrów typu są określane przez ograniczenia, jeśli istnieją, wprowadzone na nich. Parametr T typu może być zawsze konwertowany na siebie, do i z Object, i do i z dowolnego typu interfejsu. Należy pamiętać, że jeśli typ jest typem T wartości w czasie wykonywania, konwersja z T na Object lub typ interfejsu będzie konwersją boksu i konwersją z Object lub typem T interfejsu na będzie konwersja rozpboxowania. Parametr typu z ograniczeniem C klasy definiuje dodatkowe konwersje z parametru typu do C i jego klas bazowych i na odwrót. Parametr T typu z ograniczeniem Tx parametru typu definiuje konwersję na Tx i wszystkie Tx konwersje na.

Tablica, której typ elementu jest parametrem typu z ograniczeniem I interfejsu, ma takie same kowariantne konwersje tablicy co tablica, której typ elementu to I, pod warunkiem, że parametr typu ma Class również ograniczenie klasy lub (ponieważ tylko typy elementów tablicy referencyjnej mogą być kowariantne). Tablica, której typ elementu jest parametrem typu z ograniczeniem C klasy, ma takie same kowariantne konwersje tablicy co tablica, której typem elementu jest C.

Powyższe reguły konwersji nie zezwalają na konwersje z nieprzeciętnych parametrów typu na typy inne niż interfejsy, co może być zaskakujące. Przyczyną tego jest zapobieganie nieporozumieniu semantyki takich konwersji. Rozważmy na przykład następującą deklarację:

Class X(Of T)
    Public Shared Function F(t As T) As Long 
        Return CLng(t)    ' Error, explicit conversion not permitted
    End Function
End Class

Jeśli konwersja na TInteger wartość została dozwolona, można łatwo oczekiwać, że X(Of Integer).F(7) zwróci 7Lwartość . Jednak nie byłoby tak, ponieważ konwersje liczbowe są brane pod uwagę tylko wtedy, gdy typy są znane jako liczbowe w czasie kompilacji. Aby semantyka było jasne, zamiast tego należy napisać powyższy przykład:

Class X(Of T)
    Public Shared Function F(t As T) As Long
        Return CLng(CObj(t))    ' OK, conversions permitted
    End Function
End Class

konwersje User-Defined

Konwersje wewnętrzne są konwersjami zdefiniowanymi przez język (tj. wymieniony w tej specyfikacji), podczas gdy konwersje zdefiniowane przez użytkownika są definiowane przez przeciążenie CType operatora. Podczas konwertowania między typami, jeśli nie mają zastosowania konwersje wewnętrzne, zostaną uwzględnione konwersje zdefiniowane przez użytkownika. Jeśli istnieje konwersja zdefiniowana przez użytkownika, która jest najbardziej specyficzna dla typów źródłowych i docelowych, zostanie użyta konwersja zdefiniowana przez użytkownika. W przeciwnym razie wynik błędu czasu kompilacji. Najbardziej konkretna konwersja to ta, której operand jest "najbliżej" typu źródłowego i którego typ wyniku jest "najbliżej" typu docelowego. Podczas określania używanej konwersji zdefiniowanej przez użytkownika zostanie użyta najbardziej specyficzna konwersja rozszerzająca; jeśli żadna konwersja rozszerzająca nie jest najbardziej specyficzna, zostanie użyta najbardziej specyficzna konwersja zawężająca. Jeśli nie ma najbardziej konkretnej konwersji zawężającej, konwersja jest niezdefiniowana i występuje błąd czasu kompilacji.

W poniższych sekcjach opisano sposób określania najbardziej określonych konwersji. Używają one następujących terminów:

Jeśli wewnętrzna konwersja rozszerzająca istnieje z typu A na typ B, a jeśli ani AB nie są interfejsami, A to jest uwzględniana przez B, i BobejmujeA.

Najbardziej obejmującym typem w zestawie typów jest jeden typ, który obejmuje wszystkie inne typy w zestawie. Jeśli żaden pojedynczy typ nie obejmuje wszystkich innych typów, zestaw nie ma najbardziej obejmującego typu. W intuicyjnym ujęciu najbardziej obejmującym typem jest "największy" typ w zestawie — jeden typ, do którego można przekonwertować każdy z innych typów za pomocą konwersji rozszerzającej.

Najbardziej obejmującym typem zestawu typów jest jeden typ, który jest obejmujący wszystkie inne typy w zestawie. Jeśli żaden pojedynczy typ nie jest uwzględniany przez wszystkie inne typy, zestaw nie ma najbardziej objętego typu. W intuicyjnym sensie najbardziej obejmującym typem jest "najmniejszy" typ w zestawie — jeden typ, który można przekonwertować na każdy z innych typów poprzez konwersję zawężającą.

Podczas zbierania kandydata konwersji zdefiniowanych przez użytkownika dla typu T?są używane operatory konwersji zdefiniowane przez T użytkownika. Jeśli typ konwertowany na jest również typem wartości dopuszczających wartość null, zostaną zniesione dowolne operatory konwersji zdefiniowane przez Tużytkownika, które obejmują tylko typy wartości innych niż null. Operator konwersji z T do S jest zniesiony, aby być konwersją z T? na S? i jest obliczany przez konwersję T? na T, w razie potrzeby, a następnie ocenianie operatora konwersji zdefiniowanego przez użytkownika z T do S , a następnie konwertowanie S na S?, w razie potrzeby. Jeśli przekonwertowana wartość to Nothing, jednak operator konwersji zniesionej konwertuje bezpośrednio na wartość Nothing typu .S? Przykład:

Structure S
    ...
End Structure

Structure T
    Public Shared Widening Operator CType(ByVal v As T) As S
        ...
    End Operator
End Structure

Module Test
    Sub Main()
        Dim x As T?
        Dim y As S?

        y = x                ' Legal: y is still null
        x = New T()
        y = x                ' Legal: Converts from T to S
    End Sub
End Module

Podczas rozpoznawania konwersji operatory konwersji zdefiniowanych przez użytkownika są zawsze preferowane przez operatory konwersji zniesionej. Przykład:

Structure S
    ...
End Structure

Structure T
    Public Shared Widening Operator CType(ByVal v As T) As S
        ...
    End Operator

    Public Shared Widening Operator CType(ByVal v As T?) As S?
        ...
    End Operator
End Structure

Module Test
    Sub Main()
        Dim x As T?
        Dim y As S?

        y = x                ' Calls user-defined conversion, not lifted conversion
    End Sub
End Module

W czasie wykonywania ocena konwersji zdefiniowanej przez użytkownika może obejmować maksymalnie trzy kroki:

  1. Najpierw wartość jest konwertowana z typu źródłowego na typ operandu przy użyciu konwersji wewnętrznej, jeśli to konieczne.

  2. Następnie wywoływana jest konwersja zdefiniowana przez użytkownika.

  3. Na koniec wynik konwersji zdefiniowanej przez użytkownika jest konwertowany na typ docelowy przy użyciu konwersji wewnętrznej, jeśli to konieczne.

Należy pamiętać, że ocena konwersji zdefiniowanej przez użytkownika nigdy nie będzie obejmować więcej niż jednego operatora konwersji zdefiniowanego przez użytkownika.

Najbardziej specyficzna konwersja rozszerzająca

Określenie najbardziej określonego operatora konwersji rozszerzającego zdefiniowanego przez użytkownika między dwoma typami odbywa się, wykonując następujące czynności:

  1. Najpierw zbierane są wszystkie operatory konwersji kandydatów. Operatory konwersji kandydatów to wszystkie operatory konwersji rozszerzające zdefiniowane przez użytkownika w typie źródłowym i wszystkie operatory konwersji rozszerzające zdefiniowane przez użytkownika w typie docelowym.

  2. Następnie wszystkie nie dotyczy operatory konwersji zostaną usunięte z zestawu. Operator konwersji ma zastosowanie do typu źródłowego i typu docelowego, jeśli istnieje wewnętrzny operator konwersji rozszerzającej z typu źródłowego do typu operandu i istnieje wewnętrzny operator konwersji rozszerzającej z wyniku operatora do typu docelowego. Jeśli nie ma odpowiednich operatorów konwersji, nie ma konkretnej konwersji rozszerzającej.

  3. Następnie określa się najbardziej konkretny typ źródła odpowiednich operatorów konwersji:

    • Jeśli którykolwiek z operatorów konwersji konwertuje bezpośrednio z typu źródłowego, typ źródła jest najbardziej określonym typem źródła.

    • W przeciwnym razie najbardziej specyficzny typ źródła jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych operatorów konwersji. Jeśli nie można odnaleźć najbardziej objętego typu, nie ma najbardziej specyficznej konwersji rozszerzającej.

  4. Następnie określa się najbardziej konkretny typ docelowy odpowiednich operatorów konwersji:

    • Jeśli którykolwiek z operatorów konwersji konwertuje bezpośrednio na typ docelowy, typ docelowy jest najbardziej określonym typem docelowym.

    • W przeciwnym razie najbardziej specyficzny typ docelowy jest najbardziej obejmującym typem w połączonym zestawie typów docelowych operatorów konwersji. Jeśli nie można odnaleźć żadnego typu obejmującego, nie ma najbardziej konkretnej konwersji rozszerzającej.

  5. Następnie, jeśli dokładnie jeden operator konwersji konwertuje z najbardziej określonego typu źródła na najbardziej konkretny typ docelowy, jest to najbardziej konkretny operator konwersji. Jeśli istnieje więcej niż jeden taki operator, nie ma konkretnej konwersji rozszerzającej.

Najbardziej specyficzna konwersja zawężania

Określenie najbardziej określonego operatora konwersji zdefiniowanego przez użytkownika między dwoma typami jest realizowane przy użyciu następujących kroków:

  1. Najpierw zbierane są wszystkie operatory konwersji kandydatów. Operatory konwersji kandydatów są wszystkimi operatorami konwersji zdefiniowanymi przez użytkownika w typie źródłowym i wszystkimi operatorami konwersji zdefiniowanymi przez użytkownika w typie docelowym.

  2. Następnie wszystkie nie dotyczy operatory konwersji zostaną usunięte z zestawu. Operator konwersji ma zastosowanie do typu źródłowego i typu docelowego, jeśli istnieje operator konwersji wewnętrznej z typu źródłowego do typu operandu i istnieje operator konwersji wewnętrznej z wyniku operatora do typu docelowego. Jeśli nie ma odpowiednich operatorów konwersji, nie ma konkretnej konwersji zawężającej.

  3. Następnie określa się najbardziej konkretny typ źródła odpowiednich operatorów konwersji:

    • Jeśli którykolwiek z operatorów konwersji konwertuje bezpośrednio z typu źródłowego, typ źródła jest najbardziej określonym typem źródła.

    • W przeciwnym razie, jeśli którykolwiek z operatorów konwersji konwertuje z typów obejmujących typ źródłowy, najbardziej specyficzny typ źródła jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych tych operatorów konwersji. Jeśli nie można odnaleźć najbardziej objętego typu, nie ma najbardziej specyficznej konwersji zawężającej.

    • W przeciwnym razie najbardziej specyficzny typ źródła jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych operatorów konwersji. Jeśli nie można odnaleźć najbardziej obejmującego typu, nie ma najbardziej specyficznej konwersji zawężającej.

  4. Następnie określa się najbardziej konkretny typ docelowy odpowiednich operatorów konwersji:

    • Jeśli którykolwiek z operatorów konwersji konwertuje bezpośrednio na typ docelowy, typ docelowy jest najbardziej określonym typem docelowym.

    • W przeciwnym razie, jeśli którykolwiek z operatorów konwersji konwertuje na typy, które są objęte typem docelowym, najbardziej specyficzny typ docelowy jest najbardziej obejmującym typem w połączonym zestawie typów źródłowych tych operatorów konwersji. Jeśli nie można odnaleźć najbardziej obejmującego typu, nie ma najbardziej specyficznej konwersji zawężającej.

    • W przeciwnym razie najbardziej specyficzny typ docelowy jest najbardziej obejmującym typem w połączonym zestawie typów docelowych operatorów konwersji. Jeśli nie można odnaleźć najbardziej objętego typu, nie ma najbardziej specyficznej konwersji zawężającej.

  5. Następnie, jeśli dokładnie jeden operator konwersji konwertuje z najbardziej określonego typu źródła na najbardziej konkretny typ docelowy, jest to najbardziej konkretny operator konwersji. Jeśli istnieje więcej niż jeden taki operator, nie istnieje najbardziej specyficzna konwersja zawężania.

Konwersje natywne

Kilka konwersji jest klasyfikowanych jako konwersje natywne , ponieważ są obsługiwane natywnie przez program .NET Framework. Konwersje te są tymi, które można zoptymalizować za pomocą DirectCast operatorów i TryCast konwersji, a także innych specjalnych zachowań. Konwersje sklasyfikowane jako konwersje natywne to: konwersje tożsamości, konwersje domyślne, konwersje odwołań, konwersje tablicy, konwersje typu wartości i konwersje parametrów typu.

Typ dominujący

Biorąc pod uwagę zestaw typów, często jest konieczne w sytuacjach, takich jak wnioskowanie typu, aby określić dominujący typ zestawu. Dominujący typ zestawu typów jest określany przez najpierw usunięcie wszystkich typów, do których co najmniej jeden inny typ nie ma niejawnej konwersji. Jeśli w tym momencie nie ma żadnych typów, nie ma typu dominującego. Dominujący typ jest wówczas najbardziej obejmujący pozostałe typy. Jeśli istnieje więcej niż jeden typ, który jest najbardziej obejmujący, nie ma dominującego typu.