Подтвердить что ты не робот

This == null внутри метода экземпляра .NET - почему это возможно?

Я всегда думал, что невозможно, чтобы this был пустым внутри тела метода экземпляра. Следуя простой программе, это доказывает, что это возможно. Это какое-то документальное поведение?

class Foo
{
    public void Bar()
    {
        Debug.Assert(this == null);
    }
}

public static void Test()
{            
    var action = (Action)Delegate.CreateDelegate(typeof (Action), null, typeof(Foo).GetMethod("Bar"));
    action();
}

ОБНОВЛЕНИЕ

Я согласен с ответами, в которых говорится, что этот метод документирован. Однако я не понимаю этого поведения. Тем более, что это не так, как С#.

Мы получили отчет от кого-то (вероятно, одна из групп .NET используя С# (думал, что в то время он еще не был назван С#), который написанный код, который вызвал метод на нулевом указателе, но они didnt получить исключение, потому что метод не имел доступа к каким-либо полям (т.е. "this" был нулевым, но ничто в используемом методе). Тогда этот метод вызвал другой метод, который использовал эту точку, и бросил исключение, и последовало немного царапин на голове. После того, как они это поняли они отправили нам записку об этом. Мы думали, что возможность вызова метода в нулевом экземпляре бит странный. Питер Голде сделал несколько тестов, чтобы понять, всегда использовал callvirt, и он был достаточно мал, чтобы мы решили чтобы внести изменения.

http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx

4b9b3361

Ответ 1

Поскольку вы передаете null в firstArgument из Delegate.CreateDelegate

Итак, вы вызываете метод экземпляра для нулевого объекта.

http://msdn.microsoft.com/en-us/library/74x8f551.aspx

Если firstArgument является пустой ссылкой и метод является методом экземпляра, результат зависит от подписей типа типа делегата и Метод:

Если сигнатура типа явно включает скрытый первый параметр метода, делегат, как говорят, представляет собой открытый метод экземпляра. При вызове делегата первый аргумент в список аргументов передается параметру скрытого экземпляра Метод.

Если подписи метода и типа совпадают (т.е. все параметры типы совместимы), то делегат, как говорят, закрыт над null ссылка. Вызов делегата - это вызов вызова экземпляра метод на нулевом экземпляре, что не особенно полезно для сделать.

Ответ 2

Конечно, вы можете вызвать метод, если вы используете инструкцию IL вызова или подход делегата. Если вы попытаетесь получить доступ к полям-членам, вы получите эту нулевую ловушку, которая даст вам исключение NullReferenceException, которое вы искали.

попробовать

 int x;
 public void Bar()
 {
        x = 1; // NullRefException
        Debug.Assert(this == null);
 }

BCL даже содержит явно эти == null проверки, чтобы помочь отладки для языков, которые не используют callvirt (например, С#) все время. Подробнее см. question.

Класс String, например, имеет такие проверки. В них нет ничего загадочного, кроме того, что вы не увидите необходимости в таких языках, как С#.

// Determines whether two strings match. 
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
public override bool Equals(Object obj)
{
    //this is necessary to guard against reverse-pinvokes and
    //other callers who do not use the callvirt instruction
    if (this == null)
        throw new NullReferenceException();

    String str = obj as String;
    if (str == null) 
        return false;

    if (Object.ReferenceEquals(this, obj)) 
        return true;

    return EqualsHelper(this, str);
}

Ответ 3

Попробуйте выполнить документацию для Delegate.CreateDelegate() в msdn.

Вы "вручную" вызываете все, и вместо этого вместо передачи экземпляра для указателя this вы передаете значение null. Так что это может произойти, но вам нужно очень тяжело попробовать.

Ответ 4

this является ссылкой, поэтому нет проблемы с его существованием null с точки зрения системы типов.

Вы можете спросить, почему NullReferenceException не было выбрано. Полный список обстоятельств, когда CLR выбрасывает это исключение документировано. Ваше дело не указано. Да, это callvirt, но Delegate.Invoke (см. Здесь), а не Bar, и поэтому ссылка this -полный делегат!

Поведение, которое вы видите, имеет интересное следствие для CLR. Делегат имеет свойство Target (соответствует вашей ссылке this), что довольно часто бывает null, а именно, когда делегат статичен (представьте Bar быть статическим). Теперь, естественно, есть частное поле поддержки для свойства, называемое _target. Имеет ли _target значение null для статического делегата? Нет, это не так. В нем содержится ссылка на сам делегат. Почему не null? Поскольку нуль является законной целью делегата, как показывает ваш пример, и CLR не имеет двух вариантов указателя null, чтобы каким-то образом отличить статический делегат.

Этот бит тривиума демонстрирует, что с делегатами нулевые цели методов экземпляра не задумываются. Вы все еще можете задать конечный вопрос: но почему их нужно поддерживать?

У ранней CLR был амбициозный план стать, среди прочего, платформой выбора даже для присяжных разработчиков на С++, цель которой была достигнута сначала с Managed С++, а затем с С++/CLI. Некоторые слишком сложные функции языка были пропущены, но не было ничего сложного в том, что поддержка экземпляров экземпляров выполняется без экземпляра, что совершенно нормально в С++. Включая поддержку делегатов.

Конечным ответом является: потому что С# и CLR - это два разных мира.

Более хорошее чтение и еще лучше читать чтобы показать дизайн, позволяющий пустым экземплярам показывать его трассировки даже в очень естественных синтаксических контекстах С#.

Ответ 5

это ссылка на чтение в классах С#. Соответственно и, как и ожидалось, это можно использовать как любые другие ссылки (в режиме только для чтения)...

this == null // readonly - possible
this = new this() // write - not possible