Почему этот дополнительный метод бросает NullReferenceException в VB.NET?

От предыдущего опыта у меня создалось впечатление, что совершенно законно (хотя, возможно, не желательный) назвать дополнительные методы на пустом экземпляре. Таким образом в C#, этот код компиляции и выполнения:

// code in static class
static bool IsNull(this object obj) {
    return obj == null;
}

// code elsewhere
object x = null;
bool exists = !x.IsNull();

Однако я просто соединял немного комплекта примера кода для других членов моей группы разработчиков (мы просто обновили до.NET 3.5, и мне присвоили задача получения команды до скорости на некоторых новых возможностях, доступных нам), и я записал то, что я думал, был VB.NET, эквивалентный из вышеупомянутого кода, только чтобы обнаружить, что это на самом деле бросает a NullReferenceException. Код, который я написал, был этим:

' code in module '
<Extension()> _
Function IsNull(ByVal obj As Object) As Boolean
    Return obj Is Nothing
End Function

' code elsewhere '
Dim exampleObject As Object = Nothing
Dim exists As Boolean = Not exampleObject.IsNull()

Отладчик останавливается тут же, как будто я назвал метод экземпляра. Я делаю что-то не так (например, есть ли некоторое тонкое различие в способе, которым я определил дополнительный метод между C# и VB.NET)? Разве на самом деле не законно назвать дополнительный метод на пустом экземпляре в VB.NET, хотя это законно в C#? (Я думал бы, что это было вещью.NET в противоположность определенной для языка вещи, но возможно я был неправ.)

Кто-либо может объяснить этого мне?

23
задан Dan Tao 8 March 2010 в 16:11
поделиться

4 ответа

Вы не можете расширить тип объекта в VB.NET.

В основном, мы не позволяем вызывать методы расширения из любого выражения, которое статически типизировано как "Object". Это было необходимо для того, чтобы существующий код с поздней привязкой, который вы могли написать, не был нарушен методами расширения.

Ссылка:

13
ответ дан 29 November 2019 в 02:51
поделиться

Обновление:

Приведенный ниже ответ, похоже, относится к случаю расширения System.Object. При расширении других классов в VB не возникает NullReferenceException.

Такое поведение задумано по причине, указанной в этом Connect issue:

VB позволяет вызывать методы расширения defined на Object, но только в том случае. если переменная не является статически статически типизирована как Object.

Причина в том, что VB также поддерживает позднюю привязку, и если мы привяжемся к метод расширения, когда вы делаете вызов от переменной, объявленной как Object, тогда будет неясно, пытаетесь ли вы вызвать метод расширения. вы пытаетесь вызвать метод расширения метод или другой метод с поздней привязкой метод с тем же именем.

Теоретически мы могли бы разрешить это с помощью опции Strict On, но один из принципов опции Strict заключается в том, что она не должен изменять семантику вашего кода. Если бы это было разрешено, то изменение настройки Option Strict могло бы вызвать молчаливую перепривязку к другой метод, что привело бы к совершенно другое поведение во время выполнения.

Пример:

Imports System.Runtime.CompilerServices

Module Extensions
    <Extension()> _
    Public Function IsNull(ByVal obj As Object) As Boolean
        Return obj Is Nothing
    End Function

    <Extension()> _
    Public Function IsNull(ByVal obj As A) As Boolean
        Return obj Is Nothing
    End Function

    <Extension()> _
    Public Function IsNull(ByVal obj As String) As Boolean
        Return obj Is Nothing
    End Function

End Module

Class A
End Class

Module Module1

    Sub Main()
        ' works
        Dim someString As String = Nothing
        Dim isStringNull As Boolean = someString.IsNull()

        ' works
        Dim someA As A = Nothing
        Dim isANull As Boolean = someA.IsNull()

        Dim someObject As Object = Nothing
        ' throws NullReferenceException
        'Dim someObjectIsNull As Boolean = someObject.IsNull()

        Dim anotherObject As Object = New Object
        ' throws MissingMemberException
        Dim anotherObjectIsNull As Boolean = anotherObject.IsNull()
    End Sub

End Module

Фактически, компилятор VB создает поздний вызов связывания в случае, если ваша переменная статически типизирована как Object:

.locals init ([0] object exampleObject, [1] bool exists)
  IL_0000:  ldnull
  IL_0001:  stloc.0
  IL_0002:  ldloc.0
  IL_0003:  ldnull
  IL_0004:  ldstr      "IsNull"
  IL_0009:  ldc.i4.0
  IL_000a:  newarr     [mscorlib]System.Object
  IL_000f:  ldnull
  IL_0010:  ldnull
  IL_0011:  ldnull
  IL_0012:  call       
     object [Microsoft.VisualBasic]Microsoft.VisualBasic.
       CompilerServices.NewLateBinding::LateGet(
        object,
        class [mscorlib]System.Type,
        string,
        object[],
        string[],
        class [mscorlib]System.Type[],
        bool[])
  IL_0017:  call       object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Operators::NotObject(object)
  IL_001c:  call       bool [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions::ToBoolean(object)
  IL_0021:  stloc.1
8
ответ дан 29 November 2019 в 02:51
поделиться

Похоже, это что-то причудливое с Object, возможно, ошибка в VB или ограничение в компиляторе, может потребоваться комментарий его святейшества Джона Скита!

В основном, похоже, что он пытается поздно привязать вызов IsNull во время выполнения, а не вызвать метод расширения, что вызывает NullReferenceException. Если вы включите Option Strict, вы увидите это во время проектирования с красными загогулинами.

Замена exampleObject на что-то отличное от Object позволит вашему примеру работать, даже если значение указанного типа будет Nothing.

3
ответ дан 29 November 2019 в 02:51
поделиться

Кажется, проблема в том, что объект имеет значение null. Кроме того, если вы попробуете что-то вроде следующего, вы получите исключение, в котором говорится, что String не имеет метода расширения под названием IsNull

Dim exampleObject As Object = "Test"
Dim text As String = exampleObject.IsNull()

Я думаю, какое бы значение вы ни задали в exampleObject фреймворк знает, что это за тип. Лично я бы избегал методов расширения в классе Object, не только в VB, но и в CSharp

0
ответ дан 29 November 2019 в 02:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: