在C#中,当您在空对象上调用扩展方法时会发生什么?
这个方法是否被调用了一个空值,或者它是否给出一个空引用exception?
MyObject myObject = null; myObject.MyExtensionMethod(); // <-- is this a null reference exception?
如果是这种情况,我将永远不需要检查我的“这个”参数为空?
这将工作正常(没有例外)。 扩展方法不使用虚拟调用(即它使用“call”il指令,而不是“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)) {...}
那里显然没有空检查。
除了Marc Gravell的正确答案之外。
如果显然这个参数是空的,你可以从编译器得到一个警告:
default(string).MyExtension();
在运行时运行良好,但会产生警告: "Expression will always cause a System.NullReferenceException, because the default value of string is null"
。
正如你已经发现的那样,因为扩展方法是简单的静态方法,所以它们将被传入的null
引用被调用,而不引发NullReferenceException
。 但是,由于它们看起来像调用者的实例方法,所以它们也应该如此。 那么你应该在大多数情况下检查this
参数,如果它是null
,就抛出一个exception。 如果方法显式地处理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; } }
我也写了一篇关于这个的博客文章 。
null将被传递给扩展方法。
如果方法试图访问对象而不检查是否为空,那么是的,它会抛出一个exception。
这里的一个人写了“IsNull”和“IsNotNull”扩展方法,检查是否传递null参考。 我个人认为这是一个畸变,不应该看到一天的光,但它是完全有效的C#。
正如其他人所指出的,在空引用上调用扩展方法会导致此参数为空,并且没有其他特殊的事情会发生。 这提出了一个想法,使用扩展方法来编写保护条款。
你可以阅读这篇文章的例子: 如何减less环复杂性:警戒条款简短的版本是这样的:
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类扩展方法:
((string)null).AssertNonEmpty("null");
该调用工作正常,只是因为运行时将成功调用null引用上的扩展方法。 那么你可以使用这个扩展方法来实现guard子句而不会有混乱的语法:
public IRegisteredUser RegisterUser(string userName, string referrerName) { userName.AssertNonEmpty("userName"); referrerName.AssertNonEmpty("referrerName"); ... }
扩展方法是静态的,所以如果你不对这个MyObject做任何事情,这应该不是一个问题,快速testing应该validation它:)
当你想让自己可读和垂直的时候,没有什么黄金法则。
- 一个值得从埃菲尔说,封装到一个方法的具体代码应该对付一些input,该代码是可行的,如果遇到一些先决条件,并确保预期的输出
在你的情况 – DesignByContract被打破了…你将要执行一些null实例的逻辑。