(это == ноль) в C #! - PullRequest
       24

(это == ноль) в C #!

128 голосов
/ 21 октября 2009

Из-за ошибки, исправленной в 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 в режиме Release он генерирует исключение InvalidProgramException. (В режиме отладки работает нормально)

В VS2010 Beta 2 он не компилируется (я не пробовал Beta 1); Я узнал, что трудный путь

Есть ли другой способ сделать this == null в чистом C #?

Ответы [ 6 ]

73 голосов
/ 21 октября 2009

Это наблюдение было опубликовано на StackOverflow в другой вопрос ранее сегодня.

Marc * отличный ответ на этот вопрос указывает, что в соответствии со спецификацией (раздел 7.5.7), вы не должны иметь доступ к this в этом контексте и возможность сделать это в C # 3.0 является ошибкой. Компилятор C # 4.0 работает правильно в соответствии со спецификацией (даже в бета-версии 1 это ошибка времени компиляции):

§ 7.5.7 Этот доступ

A этот доступ состоит из зарезервированного слова this.

это-доступ:

this

A this-access разрешен только в блоке конструктора экземпляра, метода экземпляра или метода доступа к экземпляру.

24 голосов
/ 21 октября 2009

Необработанная декомпиляция (Reflector без оптимизаций) двоичного файла режима отладки:

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 

(Отражатель падает при превращении его в C #)


РЕДАКТИРОВАТЬ : Кто-нибудь (Эрик Липперт?) Знает, почему компилятор выдает ldloc?

11 голосов
/ 21 октября 2009

У меня было это! (и получил доказательства тоже)

alt text

10 голосов
/ 21 октября 2009

Это не «ошибка». Это вы злоупотребляете системой типов. Вы никогда не должны передавать ссылку на текущий экземпляр (this) кому-либо в конструкторе.

Я мог бы создать аналогичную «ошибку», вызвав виртуальный метод в конструкторе базового класса.

То, что вы можете делать что-то плохое, не означает, что ошибка , когда вас это укусит.

4 голосов
/ 21 октября 2009

Я могу ошибаться, но я почти уверен, что если ваш объект null, никогда не будет сценария, в котором применяется this.

Например, как бы вы назвали CheckNull?

Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException
0 голосов
/ 07 мая 2010

Не уверен, что это то, что вы ищете

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

...