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

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

// 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против нуля. Комментарий немного сбивает с толку, поэтому мне было интересно, что на самом деле означает этот комментарий?

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

2 ответа

Решение

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

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

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

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

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

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

Другие вопросы по тегам