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

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


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

评论

当然,除非您使用的是ASP.NET MVC,否则它将引发此错误。无法对空引用执行运行时绑定。

相关:扩展方法中的ArgumentNullException还是NullReferenceException?

#1 楼

这样就可以正常工作(也不例外)。扩展方法不使用虚拟调用(即,它使用“ 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);
}


etc

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

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


成为:

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


显然没有空检查。

评论


Marc,您所谈论的是“虚拟”调用-但是对于实例方法的非虚拟调用也是如此。我认为这里的“虚拟”一词放错了地方。

–康拉德·鲁道夫(Konrad Rudolph)
09年5月11日在9:11

@Konrad:这取决于上下文。 C#编译器通常甚至对非虚拟方法也使用callvirt,以精确地获取空检查。

–乔恩·斯基特(Jon Skeet)
09年5月11日在9:33

我指的是call和callvirt il指令之间的区别。在一次编辑中,我实际上试图对两个操作码页面进行href,但是编辑器对链接the之以鼻……

– Marc Gravell♦
09年5月11日在9:48

我不知道这种扩展方法的使用真的有什么用。仅仅因为可以做到并不意味着它是对的,正如下面的“二进制担忧者”所提到的,在我看来,至少可以说,这更像是一种畸变。

–陷阱
09年5月11日在9:56

@陷阱:如果您正在使用函数式编程,则此功能非常有用。

–罗伊·廷克(Roy Tinker)
2011年9月28日在20:07

#2 楼

除了Marc Gravell的正确答案之外。

如果很显然此参数为null,则可以从编译器获得警告:

default(string).MyExtension();


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

评论


为什么会警告“总是导致System.NullReferenceException”。实际上,它永远不会?

–tpower
09年5月11日在9:03

幸运的是,我们程序员只关心错误,而不是警告:p

– JulianR
09年5月11日在11:45

@JulianR:是的,有些这样做,有些则没有。在发行版本配置中,我们将警告视为错误。所以它根本不起作用。

– Stefan Steinegger
09年5月11日在12:24

感谢您的来信;我将在错误数据库中得到它,我们将看看是否可以针对C#4.0进行修复。 (没有保证,因为这是一个不切实际的极端案例,仅是一个警告,我们可能会对其进行修复。)

–埃里克·利珀特
09年5月11日在17:25

@Stefan:由于这是一个错误而不是“ true”警告,因此您可以使用#pragma语句代替该警告,以使代码通过发行版本。

–马丁R-L
2010年1月13日在8:34

#3 楼

正如您已经发现的那样,由于扩展方法只是简单的静态方法,因此将使用传入的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; 
  } 
}


我也写了前段时间有关此的博客文章。

评论


之所以投票,是因为它是正确的并且对我有意义(并且写得很好),而我也更喜欢@Marc Gravell在答复中描述的用法。

– qxotk
18年7月27日在21:48

#4 楼

null将被传递给扩展方法。

如果该方法尝试访问对象而不检查其是否为null,则是,它将抛出异常。

一个人在这里写了“ IsNull”和“ IsNotNull”扩展方法,检查引用是否传递为null。我个人认为这是一种畸变,不应该过时,但这是完全有效的c#。

评论


的确,对我而言,这就像问一具尸体“你还活着”,而得到的答案是“否”。尸体不能回答任何问题,也不能在空对象上“调用”方法。

–二进制担忧者
09年5月11日在10:20

我不同意Binary Worrier的逻辑,因为它可以方便地调用扩展而不必担心null引用,但类比喜剧值却为+1 :-)

–蒂姆·阿贝尔
2011年8月2日上午10:01

实际上,有时您不知道某人是否已死,所以您仍然要问,这个人可能会回答:“不,只是闭着眼睛休息”

– nurchi
2014年9月17日下午1:45

当您需要链接多个操作(例如3次以上)时,您可以(假设没有副作用)将几行无聊的样板空检查代码转换为具有“空安全”扩展方法的优美链接的单行代码。 (类似于建议的“。?”运算符,但是公认的不那么优雅。)如果扩展名不是“ null-safe”,我通常会在方法前加上“ Safe”作为前缀,因此例如,它是一个副本-方法,其名称可以为“ SafeCopy”,如果参数为null,则返回null。

– AnorZaken
2014-12-10 22:22



我用@BinaryWorrier回答笑得如此哈哈哈哈哈哈哈哈,我发现自己踢了一个尸体以检查它是否已经死了哈哈哈所以在我的想象中,谁检查了尸体是否已经死了是我而不是尸体本身,检查在我里面,主动踢它看看它是否移动。因此,一个尸体不知道它是否已经死了,世卫组织检查,知道,现在您可以辩称,您可以“插入”该身体,以一种方式告诉您它是否已经死了,我认为这是什么?扩展名。

–佐金
18年6月22日在15:30

#5 楼

正如其他人指出的那样,对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");

        ...

    }


#6 楼

该扩展方法是静态的,因此,如果您对此MyObject没有任何要求,那应该不成问题,可以通过快速测试进行验证:)

#7 楼

当您希望自己具有可读性和垂直性时,很少有黄金法则。


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

对于您的情况,
-DesignByContract损坏了……您将在空实例上执行一些逻辑。