Почему установка объекта из значения интерфейса выдает invalidCastException только в одну строку, если оператор - PullRequest
3 голосов
/ 03 мая 2019

При установке объекта массива с помощью оператора if, который содержит интерфейсы, я получаю исключение InvalidCastException и должен понять, почему

Я знаю, как исправить реальную проблему, я просто запутался, почему это происходит.

Обратите внимание, что customerOne и Two - это интерфейсы (то есть ICustomer), а customer - это массив "Customer" (не интерфейс)

Это работает неправильно только в случае массива, если я пытаюсь установить для одного объекта значение интерфейса, он отлично работает

Dim customerOne as ICustomer
Dim customerTwo as ICustomer

--- ^^ они заполняются ... затем мы создаем массив:

Dim customers as Customer()

Теперь странная часть ...

customers = {customerOne, customerTwo}

выше работает отлично

customers = If(myBool, {customerOne,customerTwo}, {customerOne,customerTwo})

Вышеприведенный сбой с InvalidCastException

Я ожидаю, что оператор If будет возвращать то же значение, что и рабочий пример, и, следовательно, не выбрасывать исключение InvalidCastException

Кто-нибудь понимает причину такого поведения?

1 Ответ

6 голосов
/ 03 мая 2019

Ваш код компилируется только потому, что у вас есть Option Strict Off, что позволяет определенным ошибкам проскользнуть под RADAR во время компиляции и обнаруживаться только через исключения во время выполнения.Если вы включите Option Strict On (что я, и большинство других, предложили бы вам сделать), событие, которое эта строка не компилирует:

customers = {customerOne, customerTwo}

Ошибка, которую он дает:

BC30512 Option Strict On запрещает неявное преобразование из 'ICustomer' в 'Customer'

Причина этого заключается в том, что выражение {customerOne, customerTwo} вычисляется в массив из ICustomer объектов, поскольку этотип переменных.Во время компиляции невозможно, чтобы компилятор знал, что эти две переменные определенно будут ссылаться на объекты Customer, поскольку теоретически они могут ссылаться на любой тип объекта, который реализует интерфейс.Поэтому лучшее, что он может сделать, - это определить тип массива на основе переменных, заданных в его инициализаторе.

Итак, это выражение оценивается как массив ICustomer(), но переменная, которой вы пытаетесь присвоить его, - это массив Customer().Поскольку Customer является более конкретным, чем ICustomer, это назначение не разрешается автоматически.Чтобы получить его для компиляции, вам нужно явно привести его.Либо вы можете привести элементы в инициализаторе массива, чтобы он вычислялся к нужному типу массива:

customers = {DirectCast(customerOne, Customer), DirectCast(customerTwo, Customer)}

Или, используя небольшой LINQ, вы можете позволить ему вычислять неправильный типмассив, а затем просто приведите весь массив:

customers = {customerOne, customerTwo}.Cast(Of Customer)().ToArray()

Однако ни один из этих параметров не является безопасным.Они оба допускают исключения проверки типов во время выполнения.Поэтому, если это вообще возможно, было бы лучше переписать ваш код таким образом, чтобы он позволял компилятору безопасно выполнять все проверки типов во время компиляции.Например, если вы измените переменную customers на массив ICustomer вместо массива конкретного типа Customer, то он будет работать нормально:

Option Strict On

Public Module MyModule
    Public Sub Main()
        Dim myBool As Boolean = False
        Dim customerOne As ICustomer = New Customer()
        Dim customerTwo As ICustomer = New Customer()
        Dim customers As ICustomer()

        ' Both of these lines compile fine because the arrays are all ICustomer()
        customers = {customerOne, customerTwo}
        customers = If(myBool, {customerOne, customerTwo}, {customerOne, customerTwo})
    End Sub

    Public Class Customer
        Implements ICustomer
    End Class

    Public Interface ICustomer
    End Interface
End Module

Насколькопричина, по которой троичный оператор If сбрасывает его ... ну, это сложнее и идет мне в голову.Я стараюсь избегать использования Option Strict Off любой ценой, поэтому, как именно это работает, я не знаю.Однако на высоком уровне оператор If добавляет дополнительный уровень оценки и вывода типов в середине, что мешает попыткам VB сделать автоматическое преобразование типов за вас.Например, если вы сделаете это с Option Strict Off, оно будет работать:

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
customers = {customerOne, customerTwo}

И если вы сделаете это, вы увидите, что то, что {customerOne, customerTwo} оценивает, на самом деле является массивом ICustomer():

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
Dim temp As Object = {customerOne, customerTwo}
Console.WriteLine(temp.GetType().Name)

Но, если вы сделаете это, во время выполнения будет сгенерировано исключение:

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
Dim temp As Object = {customerOne, customerTwo}
customers = temp

Выдается исключение:

System.InvalidCastException: Невозможно привести объект типа 'ICustomer []' к типу "Customer []"

. Просто разделив его на два этапа, заставив сначала выполнить оценку входного массива, онвыходит из строя.Это будет работать только при создании массива и назначении переменной в той же команде.На самом деле, происходит сбой с тем же исключением, даже если вы указываете правильный тип для этой промежуточной переменной:

Dim customerOne As ICustomer = New Customer()
Dim customerTwo As ICustomer = New Customer()
Dim customers As Customer()
Dim temp As ICustomer() = {customerOne, customerTwo}
customers = temp

И это, по сути, то, что делает оператор If.Он делится на два этапа, где оператор If должен сначала оценить свои операнды, чтобы определить, к какому типу он сам относится, (например, к тому, что он возвращает, если он был методом).И этот дополнительный шаг в середине мешает VB автоматически выполнять преобразование типов.Конкретно почему, я не могу сказать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...