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

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

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

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

11.05.2009 08:35:27
Если, конечно, вы имеете дело с ASP.NET MVC, который выдаст эту ошибку Cannot perform runtime binding on a null reference.
Mrchief 8.01.2015 03:58:49
7 ОТВЕТОВ
РЕШЕНИЕ

Это будет работать нормально (не исключение). Методы расширения не используют виртуальные вызовы (т.е. он использует инструкцию 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)) {...}

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

384
11.05.2009 09:49:06
Марк, вы говорите о «виртуальных» вызовах - но то же самое относится и к невиртуальным вызовам в методах экземпляра. Я думаю, что слово «виртуальный» здесь неуместно.
Konrad Rudolph 11.05.2009 09:11:33
@ Конрад: это зависит от контекста. Компилятор C # обычно использует callvirt даже для не виртуальных методов, именно для получения нулевой проверки.
Jon Skeet 11.05.2009 09:33:13
Я имел в виду разницу между звонком и звонком иль инструкций. В одном редактировании я действительно пытался создать две страницы с кодами операций, но редактор сорвался по ссылкам ...
Marc Gravell♦ 11.05.2009 09:48:23
Я не понимаю, как такое использование методов расширения может быть полезным. То, что это можно сделать, не означает, что это правильно, и, как упоминалось ниже, Binary Worrier, для меня это выглядит как отклонение, если не сказать больше.
Trap 11.05.2009 09:56:24
@Trap: эта функция великолепна, если вы увлекаетесь программированием в функциональном стиле.
Roy Tinker 28.09.2011 20:07:16

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

3
11.05.2009 08:38:33

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

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

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

17
11.05.2009 09:47:01
Действительно, для меня это все равно, что спросить труп «Ты жив» и получить ответ «нет». Труп не может ответить ни на один вопрос, также вы не должны быть в состоянии «вызвать» метод для нулевого объекта.
Binary Worrier 11.05.2009 10:20:04
Я не согласен с логикой Binary Worrier, так как это удобно для возможности вызывать расширения, не беспокоясь о пустых ссылках, но +1 для значения комедии аналогии :-)
Tim Abell 2.08.2011 10:01:08
На самом деле, иногда вы не знаете, умер ли кто-то, поэтому вы все равно спрашиваете, и этот человек может ответить: «Нет, просто
nurchi 17.09.2014 01:45:07
Когда вам нужно объединить несколько операций (скажем, 3+), вы можете (при условии отсутствия побочных эффектов) превратить несколько строк скучного стандартного кода проверки на нуль в элегантно соединенную строку с «нулевыми» методами расширения. (Похоже на предложенный оператор «.?», Но, по общему признанию, не такой элегантный.) Если не очевидно, что расширение является «нулевым», я обычно добавляю методу метод «Safe», поэтому, если, например, это copy- Метод, его имя может быть «SafeCopy», и он будет возвращать нуль, если аргумент был ноль.
AnorZaken 10.12.2014 22:22:12
Я так сильно смеялся с ответом @BinaryWorrier, хахахаха, я видел, как пинал тело, чтобы проверить, мертв ли ​​он или нет, хахаха. Так что в МОЕМ воображении, кто действительно проверял, было ли тело мертвым, был я, а не само тело, реализация Чек был во мне, активно пинал его, чтобы увидеть, если он движется. Итак, тело не знает, мёртвый он или нет, ВОЗ проверяет, знает, теперь вы можете утверждать, что вы могли бы «подключить» к телу способ, которым он мог бы сказать вам, мёртв ли ​​он или нет, и что, по моему мнению, это то, что Расширение для.
Zorkind 22.06.2018 15:30:27

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

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

default(string).MyExtension();

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

50
11.05.2009 12:25:15
Почему бы это предупредить "всегда вызывать исключение System.NullReferenceException". Когда на самом деле этого никогда не будет?
tpower 11.05.2009 09:03:44
К счастью, мы, программисты, заботимся только об ошибках, а не о предупреждениях: p
JulianR 11.05.2009 11:45:21
@JulianR: Да, некоторые делают, некоторые нет. В нашей конфигурации сборки выпуска мы рассматриваем предупреждения как ошибки. Так что это просто не работает.
Stefan Steinegger 11.05.2009 12:24:42
Спасибо за примечание; Я получу это в базе данных ошибок, и мы посмотрим, сможем ли мы это исправить для C # 4.0. (Никаких обещаний - поскольку это нереалистичный угловой случай и просто предупреждение, мы могли бы попытаться его исправить.)
Eric Lippert 11.05.2009 17:25:43
@Stefan: Так как это ошибка, а не «истинное» предупреждение, вы можете использовать оператор #pragma, чтобы подавить предупреждение, чтобы код прошел вашу сборку релиза.
Martin R-L 13.01.2010 08:34:37

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

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

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

-1
11.05.2009 09:07:41

Как вы уже обнаружили, поскольку методы расширения являются просто прославленными статическими методами, они будут вызываться с 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; 
  } 
}

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

24
24.11.2010 15:12:40
Проголосовал за это, потому что это правильно и имеет смысл для меня (и хорошо написано), а я также предпочитаю использование, описанное в ответе @Marc Gravell.
qxotk 27.07.2018 21:48:42

Как отмечали другие, вызов метода расширения по нулевой ссылке приводит к тому, что аргумент 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");

        ...

    }
7
20.08.2015 12:01:22