23

以前の経験から、null インスタンスで拡張メソッドを呼び出すことは (おそらくお勧めではありませんが) 完全に合法であるという印象を受けていました。したがって、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と思われるものを書きましたが、実際には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 の間で拡張メソッドを定義する方法に微妙な違いがありますか?)。C#では合法ですが、VB.NETのnullインスタンスで拡張メソッドを呼び出すことは実際には合法ではありませんか? (これは言語固有のものではなく .NET のものだと思っていたでしょうが、おそらく私は間違っていました。)

誰か私にこれを説明できますか?

4

4 に答える 4

14

VB.NET でオブジェクト タイプを拡張することはできません。

主に、「オブジェクト」として静的に型指定された式から拡張メソッドを呼び出すことは許可されていません。これは、作成した可能性のある既存のレイト バインド コードが拡張メソッドによって壊れないようにするために必要でした。

参照:

于 2010-03-08T16:41:15.983 に答える
8

アップデート:

System.Object以下の回答は、拡張された場合に固有のようです。他のクラスを拡張する場合NullReferenceException、VB にはありません。

この動作は、この接続の問題に記載されている理由により、設計によるものです。

VB では、変数がオブジェクトとして静的に型指定されていない場合に限り、オブジェクトで定義された拡張メソッドを呼び出すことができます。

その理由は、VB は遅延バインディングもサポートしているためです。Object として宣言された変数を呼び出すときに拡張メソッドにバインドすると、拡張メソッドを呼び出そうとしているのか、別の遅延バインディングを呼び出そうとしているのかが曖昧になります。同じ名前のバインドされたメソッド。

理論的には Strict On でこれを許可できますが、Option Strict の原則の 1 つは、コードのセマンティクスを変更してはならないということです。これが許可されている場合、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
于 2010-03-08T16:30:43.683 に答える
3

おそらくVBのバグまたはコンパイラの制限である可能性があります。ジョン・スキート法王にコメントしてもらう必要があるかもしれません。

基本的に、NullReferenceException を引き起こす拡張メソッドを呼び出すのではなく、実行時に IsNull 呼び出しを遅延バインドしようとしているようです。Option Strict をオンにすると、設計時にこれが赤い波線で表示されます。

exampleObject を Object 以外のものに変更すると、その型の値が Nothing であっても、サンプル コードを動作させることができます。

于 2010-03-08T16:32:39.413 に答える
0

問題は、オブジェクトが null であることです。また、次のようなことを試みると、String には IsNull という拡張メソッドがないという例外が発生します。

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

exampleObject にどのような値を入れても、フレームワークはそれがどのような型であるかを認識していると思います。個人的には、VB だけでなく CSharp でも Object クラスの拡張メソッドを避けたいと思います。

于 2010-03-08T16:30:34.800 に答える