空检查何时可以抛出 NullReferenceException

The*_*Fox 32 c# null nullreferenceexception

我知道一开始这似乎是不可能的,一开始对我来说也是如此,但最近我看到了这种代码抛出 a NullReferenceException,所以这绝对是可能的。

不幸的是,Google 上几乎没有任何结果可以解释诸如此类的代码何时foo == null会引发 NRE,这会使调试和理解其发生的原因变得困难。因此,为了记录这种看似奇怪的事件可能发生的可能方式。

这段代码可以以什么方式foo == null抛出NullReferenceException?

Jon*_*lis 38

在 C# 中,您可以重载运算符以在这样的比较中添加自定义逻辑。例如:

class Test
{
    public string SomeProp { get; set; }
    
    public static bool operator ==(Test test1, Test test2)
    {
        return test1.SomeProp == test2.SomeProp;
    }

    public static bool operator !=(Test test1, Test test2)
    {
        return !(test1 == test2);
    }
}
Run Code Online (Sandbox Code Playgroud)

那么这将产生一个空引用异常:

Test test1 = null;
bool x = test1 == null;
Run Code Online (Sandbox Code Playgroud)

  • 术语说明:这是*重载* - 您不能在 C# 中*覆盖*运算符。 (13认同)

小智 16

一个例子是 getter:

class Program
{
    static void Main(string[] args)
    {
        new Example().Test();
    }
}

class Example
{
    private object foo
    {
        get => throw new NullReferenceException();
    }

    public void Test()
    {
        Console.WriteLine(foo == null);
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码将产生 NullReferenceException。


Dav*_*d L 9

虽然相当深奥,但有可能通过DynamicMetaObject. 这将是可能发生这种情况的一个罕见但有趣的例子:

void Main()
{
    dynamic foo = new TestDynamicMetaObjectProvider();
    object foo2 = 0;
    
    Console.WriteLine(foo == foo2);
}

public class TestDynamicMetaObjectProvider : IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression parameter)
    {
        return new TestMetaObject(parameter, BindingRestrictions.Empty, this);
    }
}

public class TestMetaObject : DynamicMetaObject
{
    public TestMetaObject(Expression expression, BindingRestrictions restrictions)
        : base(expression, restrictions)
    {
    }

    public TestMetaObject(Expression expression, BindingRestrictions restrictions, object value)
        : base(expression, restrictions, value)
    {
    }

    public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
    {
        // note it doesn't have to be an explicit throw.  Any improper property
        // access could bubble a NullReferenceException depending on the 
        // custom implementation.
        throw new NullReferenceException();
    }
}
Run Code Online (Sandbox Code Playgroud)


Cod*_*ter 7

不是字面上的代码,但等待空任务也会抛出:

public class Program
{
    public static async Task Main()
    {
        var s = ReadStringAsync();
        if (await s == null)
        {
            Console.WriteLine("s is null");
        }
    }

    // instead of Task.FromResult<string>(null);
    private static Task<string> ReadStringAsync() => null;
}
Run Code Online (Sandbox Code Playgroud)

但是请注意,调试器可能会错误地获取抛出语句的位置。它可能会显示在相等检查中抛出的异常,而它发生在早期代码中。

  • 一个明显的原因是调试发布版本或使用过时的 PDB,而且代码还包含多个 try-catch-throw 块。另请参阅[堆栈跟踪中的错误行号](/sf/ask/174564561/) 和[堆栈跟踪中的错误行号](https://stackoverflow .com/questions/37072185/wrong-line-numbers-in-stack-trace)。 (4认同)