在 C# 中,当您在空对象上调用扩展方法时会发生什么情况?

问题描述 投票:0回答:9

方法是用空值调用还是给出空引用异常?

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

如果是这种情况,我将永远不需要检查我的“this”参数是否为空?

c# parameters null extension-methods
9个回答
449
投票

那会很好(无一例外)。扩展方法不使用虚拟调用(即它使用“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)) {...}

显然没有空检查。


53
投票

Marc Gravell 对正确答案的补充。

如果 this 参数明显为空,您可能会收到编译器的警告:

default(string).MyExtension();

在运行时运行良好,但会产生警告

"Expression will always cause a System.NullReferenceException, because the default value of string is null"
.


33
投票

正如您已经发现的那样,由于扩展方法只是美化了的静态方法,因此将使用传入的

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; 
  } 
}

前段时间我也写过一篇关于这个的博客文章


21
投票

一个 null 将被传递给扩展方法。

如果该方法尝试访问对象而不检查它是否为空,那么是的,它将抛出异常。

这里的一个人写了“IsNull”和“IsNotNull”扩展方法来检查引用是否为空。就我个人而言,我认为这是一种失常,不应该公开,但它是完全有效的 c#。


10
投票

正如其他人指出的那样,在空引用上调用扩展方法会导致 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");

        ...

    }

4
投票

myObject.MyExtensionMethod();

为空时,
myObject
永远不会抛出空引用异常...但是如果
MyExtensionMethod()
没有正确处理空值
.

https://dotnetfiddle.net/KqwLya


3
投票

扩展方法是静态的,所以如果你不对这个 MyObject 做任何事情,它应该不是问题,一个快速测试应该验证它:)


0
投票

当你想让你的文章具有可读性和垂直性时,几乎没有什么黄金法则。

  • Eiffel 值得一提的是,封装到方法中的特定代码应该针对某些输入起作用,如果满足某些先决条件并确保预期输出,该代码是可行的

你的情况 - DesignByContract 被破坏了......你将在空实例上执行一些逻辑。


0
投票

现在在扩展方法调用的 c# 规范中记录了这种边缘情况行为: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#11893-extension-method-invocations

与实例方法调用不同,当 expr 计算为空引用时不会抛出异常。相反,这个 null 值会像通过常规静态方法调用一样传递给扩展方法。由扩展方法实现来决定如何响应这样的调用。

© www.soinside.com 2019 - 2024. All rights reserved.