2010-03-08 6 views
23

이전 사례에서 필자는 null 인스턴스에서 확장 메서드를 호출하는 것이 합법적이라고 (아마도 권장하지 않지만) 인상적이었습니다. 그래서 C#에서,이 코드는 컴파일 및 실행 그러나이 확장 메서드가 VB.NET에서 NullReferenceException을 던지는 이유는 무엇입니까?

// 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 사이의 확장 메서드를 정의한 방식에 약간의 차이가 있습니까?)? 실제로는 이 아니며은 VB.NET의 null 인스턴스에 대한 확장 메서드를 호출 할 수 있습니다. C#에서는 올바르지 만? (나는이 언어가 언어적인 것에 반대되는. NET 일 것이라고 생각했지만 아마도 틀 렸습니다.)

아무도 나에게이 설명을 할 수 있습니까?

+1

이 ISNULL 방법 단지 예인가, 아니면 실제로 x.IsNull을 사용하고자 할 수 있습니다() 'x Is Nothing'또는 'x == null'대신에? – jrummell

+0

@jrummell : 단지 예일뿐입니다. 앞에서 언급했듯이 일부 팀 구성원의 확장 방법이 어떻게 작동하는지 보여주기 위해 몇 가지 예제 코드를 작성했습니다. 이 메서드는 "확장 메서드를 사용하여 실제로이 작업을 수행 할 수 있지만 권장하지는 않지만 실제로는 확장 메서드를 정적 메서드로 사용하는 방법"을 보여주기 위해 주석에 포함 시키려고했습니다.) 방법. 그런데 나는 VB에서 그것을 할 수 없다는 것을 발견했다. –

+1

발견 된 바와 같이, 이것은 늦은 바인딩에 대한 지원으로 인해 ... 당신처럼 (단) 나는 VB 바인딩 지원에 대한 지원이 없다는 것을 깨달았습니다. VB6과의 호환성 문제라고 생각합니다. 약간의 문제는 다른 개발자를위한 표준/교육에 대한 계획을 세우려는 경우 Option Strict가 많은 잠재적 인 문제를 해결하는 데 도움이된다고 생각하는 것입니다. 물론 YMMV. –

답변

13

VB.NET에서 개체 유형을 확장 할 수 없습니다.

주로, 우리는 확장 메서드는 정적으로 "개체"로 입력되는 모든 표현의 중지가되는 것을 허용하지 않습니다. 이것은 작성한 기존의 늦은 바운드 코드가 확장 메소드에 의해 손상되는 것을 막기 위해 필요했습니다.

참조 :

+0

아, 그건 이해가 되네 ... 계속해서 짜증나지만 C#/VB의 차이점/불일치. 링크 된 기사에서 가져올 중요한 문구는 다음과 같습니다. "객체를 지정하면"객체가 아닌 * 다른 것을 가져 오십시오 "라는 의미입니다. 굉장합니다. 8-) –

+0

@roygbiv : VB.NET에서 너무 자주 코딩하지 않는 사람으로서, (예를 들어, VB.NET의 후기 바인딩 기능이 상당히 작았다고 추측했을 것입니다.) 그래서 새로운 것을 가르쳐 주셔서 고맙습니다. –

+1

@DanTao : VB.NET에서 변수를'System.Object'로 선언하고 단순히 기본 형식이'System.Object' 인 클래스 참조로 동작하도록하고 싶습니다. 'Object'와 관련된 특수한 핸들링없이. – supercat

0

문제는 개체가 null 인 것 같습니다. 또한 , 당신은 다음과 같은 것을 시도하는 경우에, 당신은 문자열 ISNULL

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

난 당신이 exampleObject에 가하고 있습니다 어떤 값 생각이라는 확장 방법이없는 것을 말하는 예외를 얻을 것이다는 프레임 워크는 어떤 종류를 알고 그것은.

대답은 아래 System.Object이 연장되는 경우에 특정한 것 같다 : 나는 개인적으로 VB에서뿐만 아니라

8

업데이트 CSHARP에서뿐만 아니라 개체 클래스의 확장 방법을 피할 것. 다른 클래스를 확장하면 VB에는 NullReferenceException이 없습니다.

VB 당신이 객체에 정의 된 확장 메서드 를 호출 할 수 있지만 변수는 정적 객체로 입력 없는 경우에만 경우 :

이 문제는 Connect issue이에서 설명한 이유로 의도적으로 설계된 동작입니다.

이유는 VB는 말은 - 바인딩을 지원, 당신은 객체로 선언 된 변수 오프 전화 을 할 때 우리가 확장 메서드에 바인딩 할 경우, 다음 당신이 전화를하려는 여부 ambigous입니다입니다 확장자가 이거나 같은 늦은 바운드 메서드

은 이론적으로 우리는 엄격한에 이것을 허용 할 수 있지만, 엄격한 옵션의 원칙 중 하나는 코드의 의미를 변경하지해야한다는 것입니다. 허용 된 경우 옵션을 엄격하게 설정하면 다른 방법으로 자동 리 바인드가 발생할 수 있으므로 결과적으로 다른 런타임 동작이 발생합니다.

예 :

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 
+0

궁금하다 - VB에서 C# 방식으로 작성된 라이브러리에있는 경우 vb 호출 방식으로 C# 방식으로 호출합니까? –

+0

및 @Joel : 방금 테스트했습니다. VB에서 호출 한 C# 라이브러리에 확장 메서드를 넣었습니다. VB 코드는 여전히 NullReferenceException을 던졌습니다. 가레스 (Gareth)가 뭔가있는 것 같습니다.이 문제는 좀 더 구체적인 것에 반대되는 'System.Object'에 확장 메서드가 적용될 때만 존재합니다. –

+0

사실, 함수가 예상하는 형식이 아닌 확장 메서드를 호출하려고하는 개체의 형식입니다. 즉, IsNull (Object로 ByVal Obj)은 Dim A As String으로 작동합니다. A.IsNull은 A가 Nothing 인 경우에도 괜찮습니다. roygbiv는 왜에 관해서는 관련 연결이있는 것을 보인다. –

3

이와 기발한 무언가를 것 같다 VB에서 개체 또는 컴파일러의 제한으로 인해 Holiness Jon Skeet이 (가) 댓글을 달 수 있습니다.

기본적으로 NullReferenceException을 발생시키는 확장 메서드를 호출하는 대신 런타임에 IsNull 호출을 런타임에 바인딩하려고 시도하는 것으로 보입니다. Option Strict를 켜면 디자인 타임에 빨간 물결 모양이 나타납니다.

exampleObject를 Object 자체가 아닌 다른 것으로 변경하면 해당 유형의 값이 Nothing 인 경우에도 샘플 코드가 작동합니다.

+0

+1. 확장 메서드를 사용하면 형식이 Object가 아닌 경우에만 Nothing을 사용할 수 있습니다. 따라서 런타임시 Object의 본질과 관련이있는 것으로 보입니다. –

+0

하, 와우 ... 이제 이상 하네. –