Метод вызывается с нулевым значением или он дает исключение нулевой ссылки?
MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?
Если это так, мне никогда не нужно будет проверять мой параметр this на null?
Это будет работать нормально (не исключение). Методы расширения не используют виртуальные вызовы (т.е. он использует инструкцию 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)) {...}
где явно нет нулевой проверки.
Метод расширения является статическим, поэтому, если вы ничего не делаете с этим MyObject, это не должно быть проблемой, быстрый тест должен это проверить :)
Нуль будет передан в метод расширения.
Если метод пытается получить доступ к объекту без проверки, является ли он пустым, то да, он выдаст исключение.
Парень здесь написал методы расширения "IsNull" и "IsNotNull", которые проверяют, передана ли ссылка на ноль или нет. Лично я думаю, что это заблуждение и не должно было светить днем, но это совершенно справедливо для C #.
Дополнение к правильному ответу от Марка Гравелла.
Вы можете получить предупреждение от компилятора, если очевидно, что аргумент this равен нулю:
default(string).MyExtension();
Хорошо работает во время выполнения, но выдает предупреждение "Expression will always cause a System.NullReferenceException, because the default value of string is null"
.
Есть несколько золотых правил, когда вы хотите, чтобы они были читабельными и вертикальными.
- Стоит сказать, что Эйфель говорит, что конкретный код, инкапсулированный в метод, должен работать против некоторого ввода, этот код работоспособен, если выполнены некоторые предварительные условия и обеспечит ожидаемый результат
В вашем случае - DesignByContract не работает ... вы собираетесь выполнить некоторую логику для нулевого экземпляра.
Как вы уже обнаружили, поскольку методы расширения являются просто прославленными статическими методами, они будут вызываться с 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;
}
}
Я также написал пост в блоге об этом некоторое время назад.
Как отмечали другие, вызов метода расширения по нулевой ссылке приводит к тому, что аргумент 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");
...
}
Cannot perform runtime binding on a null reference
.