C #에서 null 개체에 대해 확장 메서드를 호출하면 어떻게됩니까?


328

메소드가 널값으로 호출되거나 널 참조 예외가 발생합니까?

MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?

이 경우 'this'매개 변수가 null인지 확인하지 않아도됩니까?


물론,이 오류가 발생하는 ASP.NET MVC를 다루고 Cannot perform runtime binding on a null reference있습니다.
Mr.

답변:


386

그것은 잘 작동합니다 (예외 없음). 확장 메서드는 가상 호출을 사용하지 않습니다 (즉, "callvirt"가 아닌 "call"il 명령어 사용). 확장 메서드에 직접 작성하지 않으면 null 검사가 없습니다. 이것은 실제로 몇 가지 경우에 유용합니다.

public static bool IsNullOrEmpty(this string value)
{
    return string.IsNullOrEmpty(value);
}
public static void ThrowIfNull<T>(this T obj, string parameterName)
        where T : class
{
    if(obj == null) throw new ArgumentNullException(parameterName);
}

기타

기본적으로 정적 호출에 대한 호출은 매우 문자 그대로입니다. 즉

string s = ...
if(s.IsNullOrEmpty()) {...}

된다 :

string s = ...
if(YourExtensionClass.IsNullOrEmpty(s)) {...}

널 검사가 분명히없는 곳.


1
Marc, "가상"호출에 대해 이야기하고 있지만 인스턴스 메소드에 대한 비가 상 호출에 대해서도 마찬가지입니다. 여기서 "가상"이라는 단어가 잘못되었다고 생각합니다.
Konrad Rudolph

6
@ Konrad : 상황에 따라 다릅니다. C # 컴파일러는 일반적으로 비가 상 메소드에 대해서도 callvirt를 사용하여 정확하게 널 검사를 수행합니다.
Jon Skeet

나는 call과 callvirt il 명령의 차이점을 언급하고있었습니다. 한 번의 편집으로 실제로 두 개의 Opcodes 페이지를 가져 오려고했지만 편집자가 링크를 통해 바프를했습니다.
Marc Gravell

2
이 확장 방법을 사용하는 것이 실제로 어떻게 유용한 지 알 수 없습니다. 그것이 이루어질 수 있다고해서 그것이 옳다는 것을 의미하지는 않으며, 이진 걱정이 아래에서 언급했듯이, 나는 가장 적게 말하는 수차처럼 보입니다.
Trap

3
@Trap :이 기능은 함수형 프로그래밍에 적합합니다.
Roy Tinker

50

Marc Gravell의 정답에 추가.

이 인수가 널인 것이 확실한 경우 컴파일러에서 경고를받을 수 있습니다.

default(string).MyExtension();

런타임에는 잘 작동하지만 경고가 발생 "Expression will always cause a System.NullReferenceException, because the default value of string is null"합니다.


32
"항상 System.NullReferenceException이 발생합니다"라는 경고가 표시되는 이유는 무엇입니까? 사실, 결코 그렇지 않습니까?
tpower

46
운 좋게도, 우리 프로그래머들은 경고가 아니라 오류 만 신경
쓰고 있습니다

7
@ JulianR : 예, 일부는 그렇지 않습니다. 릴리스 빌드 구성에서는 경고를 오류로 취급합니다. 그래서 그것은 작동하지 않습니다.
Stefan Steinegger

8
메모 주셔서 감사합니다; 버그 데이터베이스에서이 문제를 해결하고 C # 4.0에서이 문제를 해결할 수 있는지 살펴 보겠습니다. (비현실적인 코너 사례 일 뿐이며 경고 일 뿐이므로 약속하지 않을 수 있습니다.)
Eric Lippert

3
@Stefan : 버그가 아니라 "진정한"경고가 아니기 때문에 #pragma 문을 사용하여 코드가 릴리스 빌드를 통과하도록 경고를 억제 할 수 있습니다.
Martin RL

24

이미 살펴본 바와 같이 확장 메서드는 단순히 정적 메서드로 영광을주기 때문에 null참조가 전달되지 않고 전달되어 참조 가 호출됩니다 NullReferenceException. 그러나, 그들은 호출자에게 인스턴스 메소드처럼 보이기 때문에 그렇게 행동 해야 합니다 . 그런 다음 대부분의 경우 this매개 변수를 확인 하고이 경우 예외를 발생 시켜야합니다 null. 메소드가 명시 적으로 null값을 처리하고 이름이 아래 예와 같이 적절하게 표시하는 경우에는이를 수행하지 않는 것이 좋습니다.

public static class StringNullExtensions { 
  public static bool IsNullOrEmpty(this string s) { 
    return string.IsNullOrEmpty(s); 
  } 
  public static bool IsNullOrBlank(this string s) { 
    return s == null || s.Trim().Length == 0; 
  } 
}

또한 얼마 전에 이것에 관한 블로그 게시물을 작성 했습니다 .


3
나는 그것이 정확하고 나에게 이해하기 때문에 (그리고 잘 쓰여졌 기 때문에) 이것을 투표했다.
qxotk

17

확장 메소드에는 널이 전달됩니다.

메소드가 점검하지 않고 객체에 액세스하려고 시도하면 null이면, 예외가 발생합니다.

여기에있는 사람이 참조가 null을 통과했는지 여부를 확인하는 "IsNull"및 "IsNotNull"확장 메서드를 작성했습니다. 개인적으로 나는 이것이 수차라고 생각하고 오늘의 빛을 보아서는 안되지만 완벽하게 유효한 C #입니다.


18
사실, 그것은 나에게 "살아 있습니까?"라는 시체를 요구하고 "아니오"라는 대답을 얻는 것과 같습니다. 시체는 어떤 질문에도 응답 할 수 없으며, 널 객체에서 메소드를 "호출"할 수도 없습니다.
이진 걱정

14
이진 걱정의 논리에 동의하지 않습니다. null 참조에 대해 걱정하지 않고 내선 번호를 호출하는 것이 편리하지만 유추 코미디 값은 +1입니다. :-)
Tim Abell

10
실제로, 때때로 당신은 누군가가 죽었는지 알지 못하기 때문에, 당신은 여전히 ​​묻고, "아니, 그냥 내 눈을 감고 쉬는 것"이라고 대답 할 수도 있습니다.
nurchi

7
여러 작업 (예 : 3+)을 연결해야하는 경우 (부작용이 없다고 가정) 여러 라인의 보링 상용구 널 검사 코드를 "널 안전"확장 방법을 사용하여 우아하게 연결된 단일 라이너로 전환 할 수 있습니다. (제안 된 ".?"연산자와 비슷하지만 우아하지는 않습니다.) 확실하지 않은 경우 확장이 "널 안전 (null-safe)"인 경우 일반적으로 메서드 앞에 "Safe"를 접두어로 붙입니다. 메소드의 이름은 "SafeCopy"일 수 있으며 인수가 널인 경우 널을 리턴합니다.
AnorZaken December

3
나는 @BinaryWorrier 답변으로 너무 열심히 웃음 hahahaha 나는 시체가 죽은 지 아닌지를 확인하기 위해 몸을 차는 것을 보았습니다. 수표가 제게 있었는지, 적극적으로 발로 움직여서 움직이는 지 확인했습니다. 그래서 시체는 그것이 죽었는지 아닌지를 모릅니다. WHO는 확인하고 알고 있습니다. 이제 당신은 시체에 "플러그인"할 수 있다고 주장 할 수 있습니다. 확장 프로그램입니다.
Zorkind

7

다른 사람들이 지적했듯이 null 참조에서 확장 메서드를 호출하면 this 인수가 null이되고 특별한 것은 발생하지 않습니다. 이것은 확장 메소드를 사용하여 가드 절을 작성하는 아이디어를 제공합니다.

를 들어이 기사를 읽을 수 있습니다. 순환 복잡성을 줄이는 방법 : Guard Clause Short 버전은 다음과 같습니다.

public static class StringExtensions
{
    public static void AssertNonEmpty(this string value, string paramName)
    {
        if (string.IsNullOrEmpty(value))
            throw new ArgumentException("Value must be a non-empty string.", paramName);
    }
}

이것은 null 참조에서 호출 할 수있는 문자열 클래스 확장 메소드입니다.

((string)null).AssertNonEmpty("null");

런타임은 null 참조에서 확장 메서드를 성공적으로 호출하기 때문에 호출이 제대로 작동합니다. 그런 다음이 확장 메소드를 사용하여 지저분한 구문없이 guard 절을 구현할 수 있습니다.

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

        userName.AssertNonEmpty("userName");
        referrerName.AssertNonEmpty("referrerName");

        ...

    }

3

확장 메소드는 정적 이므로이 MyObject에 아무것도하지 않으면 문제가되지 않아야하며 빠른 테스트를 통해 확인해야합니다 :)


-1

읽기 쉽고 세로로 만들고 싶을 때 황금률이 ​​거의 없습니다.

  • 에펠에서 말할만한 가치가 있다고 말하면 메소드에 캡슐화 된 특정 코드는 일부 입력에 대해 작동해야하며 일부 전제 조건을 충족하고 예상 출력을 보장하면 코드를 실행할 수 있다고 말합니다

귀하의 경우-DesignByContract가 손상되었습니다 ... null 인스턴스에서 일부 논리를 수행하려고합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.