该方法是使用空值调用还是给出空引用异常?
MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?
如果是这种情况,我将永远不需要检查我的'this'参数是否为null?
该方法是使用空值调用还是给出空引用异常?
MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?
如果是这种情况,我将永远不需要检查我的'this'参数是否为null?
Answers:
这样就可以正常工作(也不例外)。扩展方法不使用虚拟调用(即,它使用“ 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)提供了正确答案。
如果显而易见此参数为null,则可以从编译器获得警告:
default(string).MyExtension();
在运行时效果很好,但是会产生警告"Expression will always cause a System.NullReferenceException, because the default value of string is null"
。
正如您已经发现的那样,由于扩展方法只是经过修饰的静态方法,因此它们将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;
}
}
空值将传递给扩展方法。
如果该方法尝试访问该对象而不检查它是否为null,则是,它将抛出异常。
一个人在这里编写了“ IsNull”和“ IsNotNull”扩展方法,用于检查引用是否传递为null。我个人认为这是一种畸变,不应该过时,但这是完全有效的C#。
正如其他人指出的那样,在null引用上调用扩展方法会使this参数为null,并且不会发生其他特殊情况。这引起了使用扩展方法编写保护子句的想法。
您可以阅读以下示例文章:如何降低环复杂性:保护子句简写为:
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
。