方法是用空值调用还是给出空引用异常?
MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?
如果是这种情况,我将永远不需要检查我的“this”参数是否为空?
那会很好(无一例外)。扩展方法不使用虚拟调用(即它使用“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 对正确答案的补充。
如果 this 参数明显为空,您可能会收到编译器的警告:
default(string).MyExtension();
在运行时运行良好,但会产生警告
"Expression will always cause a System.NullReferenceException, because the default value of string is null"
.
正如您已经发现的那样,由于扩展方法只是美化了的静态方法,因此将使用传入的
null
引用调用它们,而不会抛出 NullReferenceException
。但是,由于它们看起来像调用者的实例方法,因此它们也应该 behave 这样。然后,大多数时候,您应该检查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”扩展方法来检查引用是否为空。就我个人而言,我认为这是一种失常,不应该公开,但它是完全有效的 c#。
正如其他人指出的那样,在空引用上调用扩展方法会导致 this 参数为空,并且不会发生其他任何特殊情况。这引发了使用扩展方法编写保护子句的想法。
您可以阅读这篇文章以获取示例:How to Reduce Cyclomatic Complexity: Guard Clause 简短版本是这样的:
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");
调用工作正常只是因为运行时将在空引用上成功调用扩展方法。然后你可以使用这个扩展方法来实现 guard clauses 而无需乱七八糟的语法:
public IRegisteredUser RegisterUser(string userName, string referrerName)
{
userName.AssertNonEmpty("userName");
referrerName.AssertNonEmpty("referrerName");
...
}
myObject.MyExtensionMethod();
为空时,
myObject
永远不会抛出空引用异常...但是如果MyExtensionMethod()
没有正确处理空值.
扩展方法是静态的,所以如果你不对这个 MyObject 做任何事情,它应该不是问题,一个快速测试应该验证它:)
当你想让你的文章具有可读性和垂直性时,几乎没有什么黄金法则。
你的情况 - DesignByContract 被破坏了......你将在空实例上执行一些逻辑。
现在在扩展方法调用的 c# 规范中记录了这种边缘情况行为: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#11893-extension-method-invocations
与实例方法调用不同,当 expr 计算为空引用时不会抛出异常。相反,这个 null 值会像通过常规静态方法调用一样传递给扩展方法。由扩展方法实现来决定如何响应这样的调用。