在c#中,当你在一个空对象上调用扩展方法时会发生什么?

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

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

如果是这样的话,我将永远不需要检查我的“this”参数为空?

62715 次浏览

这将工作得很好(没有例外)。扩展方法不使用虚拟调用(即它使用“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)) {...}

这里显然没有空检查。

extensionmethod是静态的,所以如果你不给这个MyObject添加任何东西,这应该不是问题,一个快速的测试应该可以验证它:)

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

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

一个家伙在这里写了“IsNull”和“IsNotNull”扩展方法,检查引用是否传递为空。就我个人而言,我认为这是一种偏差,不应该被看到,但这是完全有效的c#。

除了Marc Gravell的正确答案。

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

default(string).MyExtension();

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

当你想让你的文章具有可读性和垂直性时,很少有黄金法则。

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

在你的情况下 -设计合同被打破了…你将在一个空实例上执行一些逻辑

正如你已经发现的,由于扩展方法只是美化的静态方法,它们将被传入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;
}
}

前段时间我也写过一篇博文

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

你可以阅读这篇文章的例子:

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");


...


}

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

https://dotnetfiddle.net/KqwLya