От предыдущего опыта у меня создалось впечатление, что совершенно законно (хотя, возможно, не желательный) назвать дополнительные методы на пустом экземпляре. Таким образом в 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 в противоположность определенной для языка вещи, но возможно я был неправ.)
Кто-либо может объяснить этого мне?
Вы не можете расширить тип объекта в VB.NET.
В основном, мы не позволяем вызывать методы расширения из любого выражения, которое статически типизировано как "Object". Это было необходимо для того, чтобы существующий код с поздней привязкой, который вы могли написать, не был нарушен методами расширения.
Ссылка:
Обновление:
Приведенный ниже ответ, похоже, относится к случаю расширения 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
Похоже, это что-то причудливое с Object, возможно, ошибка в VB или ограничение в компиляторе, может потребоваться комментарий его святейшества Джона Скита!
В основном, похоже, что он пытается поздно привязать вызов IsNull во время выполнения, а не вызвать метод расширения, что вызывает NullReferenceException. Если вы включите Option Strict, вы увидите это во время проектирования с красными загогулинами.
Замена exampleObject на что-то отличное от Object позволит вашему примеру работать, даже если значение указанного типа будет Nothing.
Кажется, проблема в том, что объект имеет значение null. Кроме того, если вы попробуете что-то вроде следующего, вы получите исключение, в котором говорится, что String не имеет метода расширения под названием IsNull
Dim exampleObject As Object = "Test"
Dim text As String = exampleObject.IsNull()
Я думаю, какое бы значение вы ни задали в exampleObject фреймворк знает, что это за тип. Лично я бы избегал методов расширения в классе Object, не только в VB, но и в CSharp