Почему String.Equals (Object obj) проверяет, не равно ли this == null?

Возможный дубликат:
Зачем проверять это! = null?

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

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

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


person myermian    schedule 16.04.2012    source источник
comment
@FlorianGreinacher: Не возможный дубликат, но в значительной степени точный дубликат, смеется. Интересно, почему он не появился в разделе «Связанные», когда я писал свой вопрос?   -  person myermian    schedule 17.04.2012


Ответы (2)


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

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

Другими словами, если бы нулевой проверки не было, EqualsHelper (или один из его вызываемых) выбросил бы исключение, а не Equals. Поскольку желательно скрыть внутреннее устройство видимой для пользователя функции, имеет смысл поставить проверку в самом начале.

person Gabe    schedule 16.04.2012

  • Такие языки, как C # и VB.NET, используют callvirt для выдачи NullReference перед вводом метода экземпляра (this == null) проверки не требуются.
  • Такие языки, как F # и Managed C ++ (большую часть времени) используют инструкцию call, где вы можете войти в метод экземпляра с нулевым указателем this. (this == null) имеет эффект.

Добавленная нулевая проверка предназначена не только для последних языков, но и для облегчения отладки, чтобы бросить в то место, где возникает ошибка (вызов метода экземпляра нулевого объекта). Если бы его не было, вы можете вызвать любой метод внутри класса без каких-либо ошибок, если это никогда не разыменование (доступ к переменным-членам). Это может зайти так далеко, что на вашем нулевом объекте несколько вызовов методов действительно сработали, и внезапно вы получите обратно исключение нулевой ссылки (метод, к которому был осуществлен доступ к данным экземпляра).

Если вы посмотрите на проверки внутри классов .NET, станет ясно, что только некоторые известные классы, такие как строка, содержат такие средства защиты. Другие методы, такие как IndexOf, не защищают от этого. Это непоследовательно, но я думаю, что снижение производительности для таких двойных нулевых проверок не стоило усилий, потому что большинство пользователей BCL являются языками, которые используют инструкцию callvirt, где вторая нулевая проверка не помогает.

person Alois Kraus    schedule 16.04.2012
comment
В F # let x = "a".Equals("b") по-прежнему компилируется в callvirt - person colinfang; 10.07.2013