C #에서 (this == null)! I null? ” + (this

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 # 으로 만드는 다른 방법이 있습니까?



답변

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

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

§ 7.5.7이 액세스

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

이 액세스 :

this

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


답변

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

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?


답변

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


답변

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

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

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


답변

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

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

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

답변

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

    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);