一开始可能看起来不可能,我最初也是这么认为的,但最近我见到过正是这种代码抛出了NullReferenceException
,所以这绝对是有可能的。
不幸的是,谷歌上几乎没有解释当像foo == null
这样的代码会抛出NRE的结果,这可能会使调试和理解发生故障的原因变得困难。因此,为了记录这种看似奇怪的情况可能发生的可能性。
哪些情况下,这个foo == null
代码会抛出NullReferenceException
呢?
一开始可能看起来不可能,我最初也是这么认为的,但最近我见到过正是这种代码抛出了NullReferenceException
,所以这绝对是有可能的。
不幸的是,谷歌上几乎没有解释当像foo == null
这样的代码会抛出NRE的结果,这可能会使调试和理解发生故障的原因变得困难。因此,为了记录这种看似奇怪的情况可能发生的可能性。
哪些情况下,这个foo == null
代码会抛出NullReferenceException
呢?
在 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);
}
}
那么这将会产生一个空引用异常:
Test test1 = null;
bool x = test1 == null;
==
中,如果两个都是“null”,则返回true,如果只有一个是true而不是两个,则返回false。如果我没记错的话,这也是一些类在.NET参考源代码中的实现方式。 - jrhobject.ReferenceEquals
,否则会出现无限循环。 - Kirk Wollis null
。 - Jon Skeet一个例子是使用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);
}
}
这段代码会产生一个NullReferenceException异常。
虽然相当深奥,但可以通过自定义的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();
}
}
这并不是说你的代码,但是等待一个空任务也会抛出异常:
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;
}
需要注意的是,调试器可能会错误地获取抛出语句的位置。它可能会显示异常在等式检查时抛出,而实际上它是在之前的代码中发生的。
Moq<IMyInterfaceWithAsync>
中错过了设置匹配条件,则会得到默认的“null”结果。这可多次令人困惑。 - Alexei Levenkovfoo == null
确实会进行运算符重载,而所涉及的运算符无法处理传递的null。我们开始考虑写作foo == null
已经过时,并且更喜欢(借鉴Visual Basic)foo is null
或!(foo is null)
,即将成为full is not null
以显式地内联一个null指针检查。operator==
实现。它不应该抛出异常,但是它确实抛出了异常。operator==
实现。但是,原帖作者并没有提及重载==
运算符的任何内容。此外,这种可能性已经在Jonesopolis的答案中得到了涵盖。 - 41686d6564 stands w. Palestine
foo
的静态类型是否实现了==
运算符? - Joe Sewellfoo is null
。这与调用ReferenceEquals(foo, null);
相同。 - ckuri