Что происходит в C #, когда вы вызываете метод расширения для нулевого объекта? - PullRequest
293 голосов
/ 11 мая 2009

Метод вызывается с нулевым значением или он дает исключение нулевой ссылки?

MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?

Если это так, мне никогда не нужно будет проверять мой параметр this на null?

Ответы [ 7 ]

356 голосов
/ 11 мая 2009

Это будет работать нормально (не исключение). Методы расширения не используют виртуальные вызовы (т. Е. Он использует инструкцию il «call», а не «callvirt»), поэтому проверка на ноль не выполняется, если вы не пишете ее самостоятельно в методе расширения. Это на самом деле полезно в нескольких случаях:

public static bool IsNullOrEmpty(this string value)
{
    return string.IsNullOrEmpty(value);
}
public static void ThrowIfNull<T>(this T obj, string parameterName)
        where T : class
{
    if(obj == null) throw new ArgumentNullException(parameterName);
}

и т.д.

По сути, вызовы статических вызовов очень буквальны - т.е.

string s = ...
if(s.IsNullOrEmpty()) {...}

становится:

string s = ...
if(YourExtensionClass.IsNullOrEmpty(s)) {...}

там, где явно нет нулевой проверки.

48 голосов
/ 11 мая 2009

Дополнение к правильному ответу Марка Гравелла.

Вы можете получить предупреждение от компилятора, если очевидно, что аргумент this равен нулю:

default(string).MyExtension();

Хорошо работает во время выполнения, но выдает предупреждение "Expression will always cause a System.NullReferenceException, because the default value of string is null".

20 голосов
/ 28 апреля 2010

Как вы уже обнаружили, поскольку методы расширения - это просто прославленные статические методы, они будут вызываться с передачей null ссылок без выброса NullReferenceException. Но, поскольку они выглядят как методы экземпляра для вызывающей стороны, они также должны вести себя как таковые. Затем вы должны в большинстве случаев проверять параметр this и выдавать исключение, если оно null. Нельзя этого делать, если метод явно заботится о значениях null и его имя указывает на это должным образом, как в примерах ниже:

public static class StringNullExtensions { 
  public static bool IsNullOrEmpty(this string s) { 
    return string.IsNullOrEmpty(s); 
  } 
  public static bool IsNullOrBlank(this string s) { 
    return s == null || s.Trim().Length == 0; 
  } 
}

Я также написал в блоге об этом некоторое время назад.

14 голосов
/ 11 мая 2009

В метод расширения будет передан ноль.

Если метод пытается получить доступ к объекту без проверки, является ли он пустым, то да, он выдаст исключение.

Парень здесь написал методы расширения "IsNull" и "IsNotNull", которые проверяют, передана ли ссылка на ноль или нет. Лично я думаю, что это заблуждение и не должно было светить днем, но это совершенно справедливо c #.

6 голосов
/ 20 августа 2015

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

Вы можете прочитать эту статью для примеров: Как уменьшить цикломатическую сложность: пункт охраны Короткая версия такова:

public static class StringExtensions
{
    public static void AssertNonEmpty(this string value, string paramName)
    {
        if (string.IsNullOrEmpty(value))
            throw new ArgumentException("Value must be a non-empty string.", paramName);
    }
}

Это метод расширения класса строки, который можно вызвать по нулевой ссылке:

((string)null).AssertNonEmpty("null");

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

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

        userName.AssertNonEmpty("userName");
        referrerName.AssertNonEmpty("referrerName");

        ...

    }
3 голосов
/ 11 мая 2009

Метод extension является статическим, поэтому, если вы ничего не делаете с этим MyObject, это не должно быть проблемой, быстрый тест должен это проверить:)

0 голосов
/ 11 мая 2009

Есть несколько золотых правил, когда вы хотите, чтобы они были читабельными и вертикальными.

  • Стоит сказать, что Эйфель говорит, что конкретный код, инкапсулированный в метод, должен работать с некоторыми входными данными, этот код работает, если выполнены некоторые предварительные условия и обеспечивает ожидаемый результат

В вашем случае - DesignByContract не работает ... вы собираетесь выполнить некоторую логику для нулевого экземпляра.

...