关于参数:在C#中,当您在null对象上调用扩展方法时会发生什么?

In C#, what happens when you call an extension method on a null object?

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

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

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


那就行了(不例外)。扩展方法不使用虚拟调用(即它使用"call"il指令,而不是"callvirt"),因此没有空检查,除非您自己在扩展方法中编写它。在一些情况下,这实际上是有用的:

1
2
3
4
5
6
7
8
9
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);
}

从根本上讲,对静态调用的调用是非常字面化的,即

1
2
string s = ...
if(s.IsNullOrEmpty()) {...}

变成:

1
2
string s = ...
if(YourExtensionClass.IsNullOrEmpty(s)) {...}

这里显然没有空校验。


除了马克·格雷威尔的正确答案。

如果很明显此参数为空,则编译器可能会发出警告:

1
default(string).MyExtension();

运行时运行良好,但会产生警告"Expression will always cause a System.NullReferenceException, because the default value of string is null"


正如您已经发现的那样,由于扩展方法只是美化静态方法,因此将使用传入的null引用调用它们,而不抛出NullReferenceException。但是,对于调用者来说,它们看起来像实例方法,因此它们也应该这样工作。然后,大多数情况下,您应该检查this参数,如果是null,则抛出异常。如果方法显式地处理null值,并且它的名称适时地表示它,如下面的示例中所示,则可以不这样做:

1
2
3
4
5
6
7
8
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;
  }
}

我以前也写过一篇关于这个的博客文章。


将向扩展方法传递空值。

如果该方法试图在不检查是否为空的情况下访问对象,那么是的,它将引发异常。

这里有个人写了"isNull"和"isNotNull"扩展方法,检查是否传递了空引用。我个人认为这是一种反常现象,不应该看到白昼之光,但这是完全正确的。


正如其他人指出的,对空引用调用扩展方法会导致此参数为空,并且不会发生其他特殊情况。这就提出了使用扩展方法来编写保护子句的想法。

您可以阅读本文中的示例:如何减少循环复杂性:guard子句的简短版本如下:

1
2
3
4
5
6
7
8
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);
    }
}

这是可以对空引用调用的字符串类扩展方法:

1
((string)null).AssertNonEmpty("null");

调用工作正常,因为运行时将在空引用上成功调用扩展方法。然后,您可以使用此扩展方法实现无语法混乱的guard子句:

1
2
3
4
5
6
7
8
9
    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

        userName.AssertNonEmpty("userName");
        referrerName.AssertNonEmpty("referrerName");

        ...

    }

extensionmethod是静态的,因此如果您对这个myObject不做任何操作,它不应该是一个问题,快速测试应该验证它:)


当你想在你的文章中保持可读性和垂直性时,很少有黄金法则。

  • 埃菲尔的一句名言是,封装到方法中的特定代码应该对某些输入有效,如果满足某些先决条件并确保预期的输出,则代码是可行的。

在你的情况下-DesignByContract已损坏…您将在一个空实例上执行一些逻辑。