C #에서 (this == null)!


129

C # 4에서 수정 된 버그로 인해 다음 프로그램이 인쇄 true됩니다. (LINQPad에서 사용해보십시오)

void Main() { new Derived(); }

class Base {
    public Base(Func<string> valueMaker) { Console.WriteLine(valueMaker()); }
}
class Derived : Base {
    string CheckNull() { return "Am I null? " + (this == null); }
    public Derived() : base(() => CheckNull()) { }
}

릴리스 모드의 VS2008에서는 InvalidProgramException이 발생합니다. (디버그 모드에서는 제대로 작동합니다)

VS2010 Beta 2에서는 컴파일되지 않습니다 (베타 1을 시도하지 않았습니다). 어려운 방법을 배웠습니다

this == null순수한 C # 으로 만드는 다른 방법이 있습니까?


3
C # 3.0 컴파일러의 버그 일 가능성이 높습니다. C # 4.0에서 작동하는 방식으로 작동합니다.
Mehrdad Afshari

82
@ SLaks : 버그의 문제는 버그가 어느 시점에서 수정 될 것으로 기대할 수 있으므로 "유용한"것은 아마 현명하지 않다는 것입니다.
AnthonyWJones

6
감사! LINQPad에 대해 몰랐습니다. 춥다!
thorn̈

8
이것이 어떤 식으로 정확하게 유용합니까?
Allen Rice

6
이 버그는 어떻게 유용 했습니까?
BlackTigerX

답변:


73

이 관찰은 오늘 초 또 다른 질문 으로 StackOverflow에 게시되었습니다 .

이 질문에 대한 Marc훌륭한 답변 은 사양 (섹션 7.5.7)에 따라 this해당 컨텍스트에서 액세스 할 수 없어야 하며 C # 3.0 컴파일러에서 액세스 할 수 있는 기능은 버그라는 것을 나타냅니다. C # 4.0 컴파일러는 사양에 따라 올바르게 작동합니다 (베타 1에서도 컴파일 시간 오류 임).

§ 7.5.7이 액세스

A는 이 액세스는 예약어로 구성된다 this.

이 액세스 :

this

(A)는 ,이 액세스는 단지 허용되는 블록 인스턴스 생성자 인스턴스 메소드 또는 인스턴스 액세서.


2
이 질문에 제시된 코드에서 키워드 "this"의 사용이 유효하지 않은 이유를 알 수 없습니다. CheckNull 메소드는 일반 인스턴스 메소드 인 nonstatic 입니다. 이러한 방법으로 "this"를 사용하는 것은 100 % 유효하며 이것을 null과 비교하는 것도 유효합니다. 오류는 기본 초기화 줄에 있습니다. 인스턴스 바인딩 된 대리자를 매개 변수로 기본 ctor에 전달하려는 시도입니다. 이것은 컴파일러의 버그 (매틱 검사의 구멍)입니다. 불가능해서는 안됩니다. : base(CheckNull())CheckNull이 정적이 아닌 경우 쓸 수 없으며 인스턴스 바운드 람다를 인라인 할 수 없어야합니다.
quetzalcoatl

4
@quetzalcoatl : thisCheckNull방법은 합법적이다. 유효하지 않은 것은 기본적으로 인스턴스 생성자 의 블록 외부에서 실행되는 암시 적 액세스 입니다 . 필자가 인용 한 사양의 일부는 키워드 의 구문 적법성에 주로 초점을 맞추고 있으며 다른 부분은이 문제를보다 정확하게 해결하지만 아마도이 사양의 일부를 개념적으로 추정하기는 쉽다는 데 동의합니다 . () => CheckNull()() => this.CheckNull()this
Mehrdad Afshari

2
죄송합니다 나는 그것을 알고 (그리고 위의 의견에 그것을 썼다) 당신도 알고 있습니다-당신은 (허용 된) 대답에서 문제의 실제 원인을 언급하지 않았습니다. 그 대답은 받아 들여졌다. 그래서 저자도 그것을 이해했다. 그러나 모든 독자가 첫눈에 인스턴스 바운드 람다 대 정적 람다를 인식하고 '이것'과 방출 된 IL의 문제에 매핑하는 데 람다가 밝고 유창 할 것이라고 의심합니다.) 이것이 3 센트를 추가 한 이유입니다. 그 외에, 나는 :) 당신과 다른 사람에 의해 발견 분석하고 설명 무슨 다른 모든 것들에 동의
케찰코아틀

24

디버그 모드 바이너리의 원시 디 컴파일 (최적화가없는 반사판)은 다음과 같습니다.

private class Derived : Program.Base
{
    // Methods
    public Derived()
    {
        base..ctor(new Func<string>(Program.Derived.<.ctor>b__0));
        return;
    }

    [CompilerGenerated]
    private static string <.ctor>b__0()
    {
        string CS$1$0000;
        CS$1$0000 = CS$1$0000.CheckNull();
    Label_0009:
        return CS$1$0000;
    }

    private string CheckNull()
    {
        string CS$1$0000;
        CS$1$0000 = "Am I null? " + ((bool) (this == null));
    Label_0017:
        return CS$1$0000;
    }
}

CompilerGenerated 메소드는 의미가 없습니다. IL (아래)을 보면 null 문자열 (!) 에서 메소드를 호출합니다 .

   .locals init (
        [0] string CS$1$0000)
    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: stloc.0 
    L_0007: br.s L_0009
    L_0009: ldloc.0 
    L_000a: ret 

릴리스 모드에서는 로컬 변수가 최적화되어 존재하지 않는 변수를 스택으로 푸시하려고 시도합니다.

    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: ret 

(C #으로 바꿀 때 리플렉터가 충돌 함)


편집 : 누군가 (Eric Lippert?) 컴파일러가 왜 ldloc?


11

나는 그것을 가지고 있었다! (그리고 증거도 얻었습니다)

대체 텍스트


2
늦었고, 코딩을 중단해야한다는 신호였다.
leppie

'this'가 무엇이든 디버거 시각화 도우미 (DebuggerDisplay)를 만들고 그 바보가 널인지 확인하십시오. : D just sayin '

10

이것은 "버그"가 아닙니다. 이것은 타입 시스템을 남용하는 것입니다. 현재 인스턴스 ( this)에 대한 참조를 생성자 내의 모든 사람 에게 전달해서는 안됩니다 .

기본 클래스 생성자 내에서 가상 메서드를 호출하여 비슷한 "버그"를 만들 수 있습니다.

당신 나쁜 일을 할 있다고 해서 조금이라도 벌레 가되는 것은 아닙니다.


14
컴파일러 버그입니다. 유효하지 않은 IL을 생성합니다. (내 답변 읽기)
SLaks

컨텍스트는 정적이므로 해당 단계에서 인스턴스 메소드 참조를 허용해서는 안됩니다.
leppie

10
@Will : 컴파일러 버그입니다. 컴파일러는 생성 해, 유효 , 검증 하는 코드에 대한 코드 또는 오류 메시지를 뱉어. 컴파일러가 사양에 따라 작동하지 않으면 버그가 있습니다.
Mehrdad Afshari

2
@ Will # 4 : 코드를 작성할 때 그 의미에 대해서는 생각하지 않았습니다. VS2010에서 컴파일을 중단했을 때 의미가 없다는 것을 깨달았습니다. –
SLaks

3
그건 그렇고, 생성자의 가상 메소드 호출은 완전히 유효한 작업입니다. 권장하지 않습니다. 논리적 재앙 이 발생할 수 있지만 결코 InvalidProgramException.
Mehrdad Afshari

3

나는 틀릴 수 있지만, 당신의 객체가 적용되는 null시나리오가 결코 없을지 확신합니다 this.

예를 들어, 어떻게 전화 CheckNull하시겠습니까?

Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException

3
생성자 인수의 람다에서. 전체 코드 스 니펫을 읽으십시오. (그리고 당신이 나를 믿지 않으면 그것을 시도하십시오)
SLaks

비록 C ++에서 객체가 생성자 내에서 참조를 가지지 않은 방법에 대해 희미하게 기억하지만, (this == null) 시나리오가 메소드 호출이 있는지 여부를 확인하는 데 사용되는지 궁금합니다. "this"에 대한 포인터를 노출시키기 전에 객체의 생성자에서 만들어집니다. C #에서 아는 한 Dispose 또는 finalization 메서드가 아니라 "this"가 null 인 경우는 없어야합니다.
jpierson

내 요점은 바로 그 아이디어 this가 컴퓨터 프로그래밍의 "코지 토, 에르고 합계"의 일종 인 null 일 가능성을 상호 배타적이라고 생각한다 . 그러므로 당신이 표현을 사용하고 this == null그것을 진실로 돌려 주겠다는 당신의 소망은 나를 잘못 인도 한 것 같습니다.
Dan Tao

다시 말해, 나는 당신의 코드를 읽었습니다. 내가 말하는 것은 당신이 무엇을 먼저 성취하려고했는지 의문입니다.
Dan Tao

이 코드는 단순히 버그를 보여 주며, 지적했듯이 전혀 쓸모가 없습니다. 실제로 유용한 코드를 보려면 두 번째 답변을 읽으십시오.
SLaks

-1

이것이 당신이 찾고있는 것인지 확실하지 않습니다.

    public static T CheckForNull<T>(object primary, T Default)
    {
        try
        {
            if (primary != null && !(primary is DBNull))
                return (T)Convert.ChangeType(primary, typeof(T));
            else if (Default.GetType() == typeof(T))
                return Default;
        }
        catch (Exception e)
        {
            throw new Exception("C:CFN.1 - " + e.Message + "Unexpected object type of " + primary.GetType().ToString() + " instead of " + typeof(T).ToString());
        }
        return default(T);
    }

예 : UserID = CheckForNull (Request.QueryString [ "UserID"], 147);


13
당신 은 질문을 완전히 오해했습니다.
SLaks

1
나는 많이 생각했다. 어쨌든 시도 할 것이라고 생각했습니다.
Scott and Dev Team
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.