Почему string.IsNullOrEmpty быстрее, чем сравнение?

MS Analyzer рекомендует использовать string.IsNullOrEmpty вместо того, чтобы сравнивать его с нулевой или пустой строкой по соображениям производительности.

Предупреждение 470 CA1820: Microsoft.Performance: замените вызов на string.operator == (string, string) 'in... с вызовом' String.IsNullOrEmpty '.

Почему? Не следует ли требовать вызова другой функции и передать ее ссылку на какой-то объект, который затем должен каким-то образом выполнить какое-либо сравнение, быть более дорогостоящим, чем само выполнение сравнения?

Пример кода

void Foo()
{ // throws a warning
    string x = "hello world";
    if (x == null || x == "")
    {
        Console.WriteLine("Empty");
    }
}

void Foo()
{ // doesn't throw it
    string x = "hello world";
    if (string.IsNullOrEmpty(x))
    {
        Console.WriteLine("Empty");
    }
}

Ответы

Ответ 1

MS Analyzer рекомендует использовать string.IsNullOrEmpty вместо того, чтобы сравнивать его с нулевой или пустой строкой по причинам производительности

Предупреждение 470 CA1820: Microsoft.Performance: замените вызов на string.operator == (string, string) 'in... с вызовом' String.IsNullOrEmpty '.

Просто прочитайте точное руководство:

Строка сравнивается с пустой строкой с использованием Object.Equals.

...

Сравнение строк с использованием свойства String.Length или метода String.IsNullOrEmpty значительно быстрее, чем использование Equals. Это связано с тем, что Equals выполняет значительно больше инструкций MSIL, чем IsNullOrEmpty, или количество выполненных команд для извлечения значения свойства Length и сравнения его с нулем.

...

Чтобы исправить нарушение этого правила, измените сравнение, чтобы использовать свойство Length и тест для нулевой строки. Если вы планируете использовать .NET Framework 2.0, используйте метод IsNullOrEmpty.

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

Опять же, из тонкого руководства:

  public void EqualsTest()
  {
     // Violates rule: TestForEmptyStringsUsingStringLength. 
     if (s1 == "")
     {
        Console.WriteLine("s1 equals empty string.");
     }
  }

  // Use for .NET Framework 1.0 and 1.1. 
  public void LengthTest()
  {
     // Satisfies rule: TestForEmptyStringsUsingStringLength. 
     if (s1 != null && s1.Length == 0)
     {
        Console.WriteLine("s1.Length == 0.");
     }
  }

Ответ 2

IsNullOrEmpty будет встроен, поэтому можно избежать накладных расходов при вызове метода. Глядя на метод, он украшен атрибутом

[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]

Я бы также добавил, что IsNullOrEmpty более ясен и более описателен с точки зрения удобочитаемости (на мой взгляд).

Что касается производительности, я был бы удивлен, если бы была какая-то реальная разница, если бы вы использовали value.Length == 0; вместо x == "". Внутри IsNullOrEmpty делает это

return value == null || value.Length == 0;

не

if (x == null || x == "")

Чтение свойства требует меньших накладных расходов, чем вычисление равенства.