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

时间:2022-09-02 09:31:50

Does the method get called with a null value or does it give a null reference exception?

是使用null值调用方法还是提供null引用异常?

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

If this is the case I will never need to check my 'this' parameter for null?

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

7 个解决方案

#1


That will work fine (no exception). Extension methods don't use virtual calls (i.e. it uses the "call" il instruction, not "callvirt") so there is no null check unless you write it yourself in the extension method. This is actually useful in a few cases:

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

Fundamentally, calls to static calls are very literal - i.e.

从根本上说,对静态呼叫的呼叫是非常直接的 - 即

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

becomes:

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

where there is obviously no null check.

哪里显然没有空检查。

#2


Addition to the correct answer from Marc Gravell.

除了Marc Gravell的正确答案。

You could get a warning from the compiler if it is obvious that the this argument is null:

如果显然this参数为null,您可以从编译器收到警告:

default(string).MyExtension();

Works well at runtime, but produces the warning "Expression will always cause a System.NullReferenceException, because the default value of string is null".

在运行时运行良好,但会产生警告“Expression将始终导致System.NullReferenceException,因为string的默认值为null”。

#3


As you've already discovered, since extension methods are simply glorified static methods, they will be called with null references passed in, without a NullReferenceException being thrown. But, since they look like instance methods to the caller, they should also behave as such. You should then, most of the time, check the this parameter and throw an exception if it's null. It's OK not to do this if the method explicitly takes care of null values and its name indicates it duly, like in the examples below:

正如您已经发现的那样,因为扩展方法只是美化的静态方法,所以在传入空引用的情况下调用它们,而不会抛出NullReferenceException。但是,因为它们看起来像调用者的实例方法,所以它们也应该这样。那么,您应该在大多数情况下检查this参数并在它为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; 
  } 
}

I've also written a blog post about this some time ago.

我前段时间也写了一篇博文。

#4


A null will be passed to the extension method.

null将被传递给扩展方法。

If the method tries to access the object without checking is it null, then yes, it will throw an exception.

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

A guy here wrote "IsNull" and "IsNotNull" extension methods that check is the reference passed null or not. Personally I think this is an aberration and shouldn't have seen light of day, but it's perfectly valid c#.

这里的一个人写了“IsNull”和“IsNotNull”扩展方法,检查是否传递null。就个人而言,我认为这是一种失常,不应该看到光明的一天,但它是完全有效的c#。

#5


As others pointed out, calling an extension method on null reference causes the this argument to be null and nothing else special will happen. This gives raise to an idea to use extension methods to write guard clauses.

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

You may read this article for examples: How to Reduce Cyclomatic Complexity: Guard Clause Short version is this:

您可以阅读本文的示例:如何减少Cyclomatic复杂性:Guard子句短版本是这样的:

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

This is the string class extension method which can be called on null reference:

这是字符串类扩展方法,可以在null引用上调用:

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

The call works fine only because runtime will successfully call the extension method on null reference. Then you can use this extension method to implement guard clauses without messy syntax:

调用工作正常,因为运行时将成功调用null引用上的扩展方法。然后你可以使用这个扩展方法来实现guard子句而不会出现凌乱的语法:

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

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

        ...

    }

#6


The extensionmethod is static, so if you don't to anything to the this MyObject it shouldn't be a problem, a quick test should verify it :)

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

#7


There are few golden rules when you want in your to be readable and vertical.

当您希望自己具有可读性和垂直性时,几乎没有什么黄金法则。

  • one worth saying from Eiffel says the specific code encapsulated into a method should work against some input, that code is workable if are met some preconditions and assure an expected output
  • 从Eiffel说的一个值得一提的是,封装到方法中的特定代码应该对某些输入起作用,如果满足一些前提条件并保证预期的输出,那么代码是可行的

In your case - DesignByContract is broken ... you are going to perform some logic on a null instance.

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

#1


That will work fine (no exception). Extension methods don't use virtual calls (i.e. it uses the "call" il instruction, not "callvirt") so there is no null check unless you write it yourself in the extension method. This is actually useful in a few cases:

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

Fundamentally, calls to static calls are very literal - i.e.

从根本上说,对静态呼叫的呼叫是非常直接的 - 即

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

becomes:

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

where there is obviously no null check.

哪里显然没有空检查。

#2


Addition to the correct answer from Marc Gravell.

除了Marc Gravell的正确答案。

You could get a warning from the compiler if it is obvious that the this argument is null:

如果显然this参数为null,您可以从编译器收到警告:

default(string).MyExtension();

Works well at runtime, but produces the warning "Expression will always cause a System.NullReferenceException, because the default value of string is null".

在运行时运行良好,但会产生警告“Expression将始终导致System.NullReferenceException,因为string的默认值为null”。

#3


As you've already discovered, since extension methods are simply glorified static methods, they will be called with null references passed in, without a NullReferenceException being thrown. But, since they look like instance methods to the caller, they should also behave as such. You should then, most of the time, check the this parameter and throw an exception if it's null. It's OK not to do this if the method explicitly takes care of null values and its name indicates it duly, like in the examples below:

正如您已经发现的那样,因为扩展方法只是美化的静态方法,所以在传入空引用的情况下调用它们,而不会抛出NullReferenceException。但是,因为它们看起来像调用者的实例方法,所以它们也应该这样。那么,您应该在大多数情况下检查this参数并在它为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; 
  } 
}

I've also written a blog post about this some time ago.

我前段时间也写了一篇博文。

#4


A null will be passed to the extension method.

null将被传递给扩展方法。

If the method tries to access the object without checking is it null, then yes, it will throw an exception.

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

A guy here wrote "IsNull" and "IsNotNull" extension methods that check is the reference passed null or not. Personally I think this is an aberration and shouldn't have seen light of day, but it's perfectly valid c#.

这里的一个人写了“IsNull”和“IsNotNull”扩展方法,检查是否传递null。就个人而言,我认为这是一种失常,不应该看到光明的一天,但它是完全有效的c#。

#5


As others pointed out, calling an extension method on null reference causes the this argument to be null and nothing else special will happen. This gives raise to an idea to use extension methods to write guard clauses.

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

You may read this article for examples: How to Reduce Cyclomatic Complexity: Guard Clause Short version is this:

您可以阅读本文的示例:如何减少Cyclomatic复杂性:Guard子句短版本是这样的:

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

This is the string class extension method which can be called on null reference:

这是字符串类扩展方法,可以在null引用上调用:

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

The call works fine only because runtime will successfully call the extension method on null reference. Then you can use this extension method to implement guard clauses without messy syntax:

调用工作正常,因为运行时将成功调用null引用上的扩展方法。然后你可以使用这个扩展方法来实现guard子句而不会出现凌乱的语法:

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

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

        ...

    }

#6


The extensionmethod is static, so if you don't to anything to the this MyObject it shouldn't be a problem, a quick test should verify it :)

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

#7


There are few golden rules when you want in your to be readable and vertical.

当您希望自己具有可读性和垂直性时,几乎没有什么黄金法则。

  • one worth saying from Eiffel says the specific code encapsulated into a method should work against some input, that code is workable if are met some preconditions and assure an expected output
  • 从Eiffel说的一个值得一提的是,封装到方法中的特定代码应该对某些输入起作用,如果满足一些前提条件并保证预期的输出,那么代码是可行的

In your case - DesignByContract is broken ... you are going to perform some logic on a null instance.

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