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