Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/272.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C#中,当对空对象调用扩展方法时会发生什么?_C#_Parameters_Null_Extension Methods - Fatal编程技术网

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

在C#中,当对空对象调用扩展方法时会发生什么?,c#,parameters,null,extension-methods,C#,Parameters,Null,Extension Methods,该方法是用null值调用的还是给出了null引用异常 MyObject myObject = null; myObject.MyExtensionMethod(); // <-- is this a null reference exception? MyObject MyObject=null; myObject.MyExtensionMethod();// 那很好(没有例外)。扩展方法不使用虚拟调用(即,它使用“call”il指令,而不是“callvirt”),因此没有空检查,除非您

该方法是用null值调用的还是给出了null引用异常

MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?
MyObject MyObject=null;
myObject.MyExtensionMethod();// 那很好(没有例外)。扩展方法不使用虚拟调用(即,它使用“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(YourExtensionClass.IsNullOrEmpty(s)) {...}

显然没有空检查的地方。

扩展方法是静态的,因此,如果您没有对此MyObject执行任何操作,这应该不是问题,应该进行快速测试以验证它:)

将向扩展方法传递空值

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


这里有个人写了“IsNull”和“IsNotNull”扩展方法,检查传递的引用是否为null。就我个人而言,我认为这是一种反常现象,不应该被认为是天方夜谭,但这是对马克·格雷威尔正确答案的完美补充。

如果此参数明显为null,则可能会从编译器中得到警告:

default(string).MyExtension();

在运行时运行良好,但产生警告
“表达式将始终导致System.NullReferenceException,因为字符串的默认值为null”

当您希望文本可读且垂直时,很少有黄金规则

  • Eiffel值得一提的一点是,封装到方法中的特定代码应该针对某些输入工作,如果满足某些先决条件并确保预期输出,则该代码是可行的
就你而言
-DesignByContract被破坏了。。。您将在一个空实例上执行一些逻辑。

正如您已经发现的,由于扩展方法只是美化的静态方法,因此将通过传入的
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引用调用扩展方法会导致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);
    }
}
这是可以在null引用上调用的字符串类扩展方法:

((string)null).AssertNonEmpty("null");
该调用工作正常,只是因为运行时将在null引用上成功调用扩展方法。然后,您可以使用此扩展方法实现guard子句,而无需混乱的语法:

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

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

        ...

    }

myObject.MyExtensionMethod()myObject
为Null时,code>永远不会引发Null引用异常但是如果
MyExtensionMethod()
不能正确处理null,它将抛出异常


为什么它会发出警告“总是导致System.NullReferenceException”。事实上,它永远不会发生?幸运的是,我们程序员只关心错误,而不关心警告:p@JulianR:是的,有些有,有些没有。在我们的发布版本配置中,我们将警告视为错误。所以它就是不起作用。谢谢你的留言;我会把它放在bug数据库中,看看是否能为C#4.0修复它。(没有承诺——因为这是一个不切实际的极端情况,仅仅是一个警告,我们可能会冒险去修复它。)@Stefan:因为这是一个bug,而不是一个“真实”的警告,你可以使用一个#pragma语句来抑制警告,让代码通过你的发布版本。Marc,你说的是“虚拟的”调用–但对实例方法的非虚拟调用也是如此。我认为“虚拟”这个词放错地方了。@Konrad:这取决于上下文。C#编译器通常使用callvirt,即使对于非虚拟方法也是如此,这恰恰是为了获得空检查。在一次编辑中,我实际上尝试了href这两个操作码页面,但编辑器在链接处发出了呕吐声……我真的看不出这种扩展方法的使用有什么用处。仅仅因为它可以做到并不意味着它是正确的,正如下面提到的二进制担忧者所说,至少在我看来,它更像是一种反常现象。@Trap:如果你喜欢函数式编程,这个功能非常棒。事实上,对我来说,这就像问一具尸体“你还活着吗”,然后得到一个“不”的答案。尸体不能回答任何问题,你也不能“调用”空对象上的方法。我不同意Binary Worrier的逻辑,因为它可以方便地调用扩展而不必担心空引用,但是+1表示类比喜剧值:-)实际上,有时你不知道某人是否死了,所以你仍然会问,当您需要链接多个操作(比如3+)时,用户可能会回答,“不,只是闭上眼睛休息一下”,您可以(假设没有副作用)将几行枯燥的样板文件空检查代码转换为一行优雅的链接,并使用“空安全”扩展方法。(类似于建议的“?”-运算符,但无可否认没有那么优雅。)如果扩展名不明显是“null-safe”,我通常会在方法前面加上“safe”前缀,因此,例如,如果它是一个复制方法,它的名称可以是“SafeCopy”,如果参数为null,它将返回null。我笑得很厉害,用@BinaryWorrier answer哈哈哈,我看到自己踢一具尸体来检查它是否死了哈哈哈,所以在我的想象中,谁来检查尸体是否死了是我,而不是尸体本身,检查的实现在我身上,积极地踢它,看看它是否死了
    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

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

        ...

    }