(this == null) в С#!
Из-за ошибки, зафиксированной в С# 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 в режиме Release он генерирует исключение InvalidProgramException. (В режиме отладки он работает нормально)
В VS2010 Beta 2 он не компилируется (я не пробовал Beta 1); Я узнал, что трудный путь
Есть ли другой способ сделать this == null
в чистом С#?
Ответы
Ответ 1
Это наблюдение было опубликовано на StackOverflow в еще одном вопросе ранее.
Marc отличный ответ на этот вопрос указывает, что согласно спецификации (раздел 7.5.7), вы не должны иметь доступ к this
в этом контексте, и возможность сделать это в компиляторе С# 3.0 является ошибкой. Компилятор С# 4.0 ведет себя корректно в соответствии со спецификацией (даже в Beta 1, это ошибка времени компиляции):
§ 7.5.7 Этот доступ
Этот доступ состоит из зарезервированного слова this
.
доступа:
this
Этот доступ разрешен только в блоке конструктора экземпляра, метода экземпляра или экземпляра экземпляра.
Ответ 2
Необработанная декомпиляция (рефлектор без оптимизаций) двоичного режима отладки:
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 (ниже), он вызывает метод в пустой строке (!).
.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
В режиме Release локальная переменная оптимизирована, поэтому она пытается вставить несуществующую переменную в стек.
L_0000: ldloc.0
L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
L_0006: ret
(Отражатель падает при повороте в С#)
EDIT: Кто-нибудь (Эрик Липперт?) знает, почему компилятор испускает ldloc
?
Ответ 3
У меня это было! (и получил доказательство тоже)
![alt text]()
Ответ 4
Это не "ошибка". Это вы злоупотребляете системой типов. Вы никогда не должны передавать ссылку на текущий экземпляр (this
) любому, кто находится внутри конструктора.
Я мог бы создать подобную "ошибку", вызвав виртуальный метод в конструкторе базового класса.
Просто потому, что вы можете сделать что-то плохое, не означает его ошибка, когда вы получите бит.
Ответ 5
Я мог ошибаться, но я уверен, что если ваш объект null
, никогда не будет сценария, в котором применяется this
.
Например, как бы вы назвали CheckNull
?
Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException
Ответ 6
Не уверен, что это то, что вы ищете
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);
}
example: UserID = CheckForNull (Request.QueryString [ "UserID" ], 147);