C#의 (이 == null)!
-
05-07-2019 - |
문제
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 베타 2에서는 컴파일하지 않습니다 (베타 1을 시도하지 않았습니다). 나는 어려운 방법을 배웠다
다른 방법이 있습니까? this == null
순수한 C#에서?
해결책
이 관찰은 stackoverflow에 게시되었습니다 다른 질문 오늘 일찍.
마크'에스 그 질문에 대한 큰 대답 사양 (섹션 7.5.7)에 따라 액세스 할 수 없음을 나타냅니다. this
이러한 맥락에서 C# 3.0 컴파일러에서 그렇게하는 능력은 버그입니다. C# 4.0 컴파일러는 사양에 따라 올바르게 작동합니다 (베타 1에서도 컴파일 타임 오류입니다).
§ 7.5.7이 액세스
ㅏ 이 액세스 예약 된 단어로 구성됩니다
this
.이 액세스 :
this
ㅏ 이 액세스 만 허용됩니다 차단하다 인스턴스 생성자, 인스턴스 메소드 또는 인스턴스 액세서의
다른 팁
디버그 모드 바이너리의 원시 디 컴파일 (최적화가없는 반사기)은 다음과 같습니다.
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;
}
}
컴파일러링 된 방법은 의미가 없습니다. 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
) 생성자 내의 모든 사람에게.
기본 클래스 생성자 내에서 가상 메소드를 호출하여 유사한 "버그"를 만들 수 있습니다.
당신 때문에 ~할 수 있다 나쁜 일을하는 것이 a를 의미하지는 않습니다 벌레 당신이 그것에 대해 조금 얻을 때.
나는 틀릴 수 있지만 당신의 대상이 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);