在C#中检查对象是否为空

315

如果一个对象为空,我希望阻止进一步的处理。

在下面的代码中,我通过以下方式检查对象是否为空:

if (!data.Equals(null))

并且

if (data != null)

然而,在 dataList.Add(data) 处我收到了一个 NullReferenceException。如果对象为空,它甚至不应该进入 if 语句!因此,我想问这种检查对象是否为空的方式是否正确:
public List<Object> dataList;
public  bool AddData(ref Object data)
    bool success = false;
    try
    {
        // I've also used "if (data != null)" which hasn't worked either
        if (!data.Equals(null))
        {
           //NullReferenceException occurs here ...
           dataList.Add(data);
           success = doOtherStuff(data);
        }
    }
    catch (Exception e)
    {
        throw new Exception(e.ToString());
    }
    return success;
}

如果这是检查对象是否为空的正确方式,那我做错了什么(如何防止在对象上进行进一步处理以避免 NullReferenceException)?


15
在抛出异常时应使用 throw e; 而不是 throw new Exception(e.ToString()); - Nix
23
在C#中,您应该始终在空值检查中使用 != null。如果对象为空,.Equals 将始终引发异常。 - Kyle Trauberman
58
@Nix:throw e; 没有太大的改善作用。另一方面,throw;... - Jon
4
@developer:e.ToString()会生成一个包含错误消息、所有InnerExceptions及堆栈跟踪的字符串,因此这是一个非常沉重的异常消息。如果您(正确地!)想要保留此信息,并且将其放在它应该的位置上,请使用简单地throw; - Jon
24
目前 try/catch 块没有任何作用。大家都在说只需使用“throw”,但是如果您对异常没有做任何处理而只是重新抛出它,那么为什么还要有 try/catch 块呢?通常情况下,您捕获异常以优雅地处理它们、清理资源(最好使用 "finally" 子句)或在重新抛出异常之前进行某种记录。但这些在此代码中都没有发生,因此根本不需要 try/catch。 - David Peterson
显示剩余5条评论
21个回答

290

空的不是数据(data),而是数据列表(dataList)

你需要用以下代码创建一个:

public List<Object> dataList = new List<Object>();

更好的做法是将其声明为一个字段并将其设置为私有private,如果没有什么阻止你,还可以将其设置为只读readonly。这是一种良好的实践。
另外,检查 null 值的正确方式是if(data != null)。对于引用类型,这种检查是无处不在的; 即使 Nullable<T> 重写了等号运算符以获得更方便的表示nullable.HasValue的方法来检查 null 值。
如果你使用if(!data.Equals(null)),那么如果data == null,你会得到一个 NullReferenceException,这有点滑稽,因为避免此异常本来就是最初的目标。
你还这样做:
catch (Exception e)
{
    throw new Exception(e.ToString());
}

这肯定不好。我可以想象你只是为了能够在方法内部仍然停留在调试器中而将其放在那里,如果是这样,请忽略本段。否则,不要无故捕获异常。如果您确实需要捕获异常,请使用throw;重新抛出它们。


7
我也见过使用 Object.ReferenceEquals(obj, null) 来达到同样的目的。这是为了避免被等式重载影响吗? - Luca
2
@LucaPiccioni 我已经使用它来防止在使用泛型时出现值类型错误: http://www.geekality.net/2009/11/13/generics-and-checking-for-null/ - Svish
5
我更倾向于写成 null != data。将常量放在前面可以将笨拙的打字错误 null = data 转换为编译错误,而不是意外赋值。(这种方式也适用于 ==。) - jpmc26
9
在C#中,if (data = null)已经是一个编译时错误,所以即使花费数十年才达到这一点,我们实际上不需要再特别留意它了。即便是C++编译器也会轻松地对该代码产生可能意外赋值的警告。 - Jon
Luca,你也可以通过在测试中将类型转换为“object”来避免相等性覆盖。同样的,这个答案应该改为这样说:“如果((object)data != null)”,因为它避免了当相等性被覆盖时出现的错误。 - DAG
1
只是为了扩展你的答案:每当你看到一个 NullReferenceException,通常会看到 null 对象后面跟着一个 .。以此作为经验法则,就清楚了 datalist.Add(data) 的异常只能与 datalist 有关。相比之下,对于 datalist.Add(data.Value),问题可能出在 datalistdata 上。(注:我没有看到这是一个七年前的死贴。对此我很抱歉) - Flater

187

使用 if (obj is null)

对于空使用以下之一:
   if (obj is object) 或者
   if (obj is not null) (C# > 9)

这些将忽略对象定义的任何==!=(除非当然你想要用它们进行空检查)

更多信息,请参阅C#语言参考中的is运算符


2
我想知道是否有一个“不是空”的方法?(Python会说obj is not null - sehe
156
希望他们能实现 if (obj 不为 null) :( - Nick Bull
18
对于不是 null 的情况,可以使用 if (obj is object) - yatskovsky
6
@OrnKristjansson 因为 != 和 == 可以被重载。 - mitchellJ
2
这里提醒一下阅读这些评论的人,许多VB开发人员似乎没有意识到AndOr不等同于&&||;后者是短路运算,前者则不是。语义上的等价物是AndAlso/OrElse - EKW
显示剩余6条评论

84

C# 6引入了单子空值检测 :)

before:

if (points != null) {
    var next = points.FirstOrDefault();
    if (next != null && next.X != null) return next.X;
}   
return -1;

之后:

var bestValue = points?.FirstOrDefault()?.X ?? -1;

8
我来这里寻找更好的句法来表达result = myObject == null ? null : myObject.SomeProperty,你的例子提示我写成result = myObject?.SomeProperty。哇!真是巧妙。我仍然热爱编程... - Adam Cox

28

根据您所发布的代码,数据列表"dataList"尚未实例化,因此为null。

请尝试:

    public List<Object> dataList = new List<Object>();
    public  bool AddData(ref Object data)
    bool success = false;
    try
    {
        if (!data.Equals(null))   // I've also used if(data != null) which hasn't worked either
        {
           dataList.Add(data);                      //NullReferenceException occurs here
           success = doOtherStuff(data);
        }
    }
    catch (Exception e)
    {
        throw;
    }
    return success;
}

3
另外需要补充的是,如果数据为空,它不会崩溃,您可以将 null 添加到 List<Object> 中。 - DaveShaw
8
尝试在 null 上使用 .Equals 会抛出异常。应该使用 != null。 - glosrob
1
@DaveShaw:谢谢你提醒。虽然如此,我还是想避免添加一个空对象以供后续处理,所以我仍会进行检查。 :) - developer
@glosrob:啊,我明白了!我会把所有的.Equals改成!= null。感谢你提醒我! - developer
+1,但我选择了Jon的答案,因为他更详细地解释了NullReferenceException。不过还是谢谢你!:D - developer
显示剩余2条评论

26

自从C# 9起,你可以这样做

if (obj is null) { ... }

使用 not null

if (obj is not null) { ... }

如果您需要覆盖此行为,请相应地使用==!=


22

[编辑以反映@kelton52的提示]

最简单的方法是使用object.ReferenceEquals(null, data)

因为(null==data)不能保证有效:

class Nully
{
    public static bool operator ==(Nully n, object o)
    {
        Console.WriteLine("Comparing '" + n + "' with '" + o + "'");
        return true;
    }
    public static bool operator !=(Nully n, object o) { return !(n==o); }
}
void Main()
{
    var data = new Nully();
    Console.WriteLine(null == data);
    Console.WriteLine(object.ReferenceEquals(null, data));
}

输出结果:

将''与'Nully'进行比较

真(True)

假(False)


2
实际上,我刚试过了,而且这个备注“隐含的优点是它忽略了数据类中可能存在的任何覆盖,比如'operator !='。”似乎并不正确。 - Kelly Elton

10
不,你应该使用!=。如果data实际上是空值,则尝试在null上调用Equals方法后,你的程序将会因为NullReferenceException而崩溃。同时请意识到,如果你特别想检查引用相等性,你应该使用Object.ReferenceEquals方法,因为你不知道如何实现Equals方法。
你的程序崩溃是因为dataList是空值,因为你从未初始化它。

8

在 C# 9(2020 年)中,您现在可以使用以下代码检查参数是否为 null:

if (name is null) { }

if (name is not null) { }

您可以在此处获取更多信息。


7
在这种情况下,问题不是data为空,而是dataList本身为空。
在声明dataList的地方,您应该创建一个新的List对象并将其赋值给变量。
List<object> dataList = new List<object>();

6
截至 C# 8,您可以使用“空”属性模式(配合模式匹配)来确保对象不为空:
if (obj is { })
{
    // 'obj' is not null here
}

这种方法意味着“如果对象引用某个实例”(即它不为null)。
你可以将其视为以下情况的相反:if (obj is null)...,当对象未引用任何实例时返回true。
有关C# 8.0中模式的更多信息,请阅读此处

看起来每次运行时都会分配一个新对象,这会增加不必要的GC压力/是一种反模式。 - Joseph Lennox
这是一个假设还是事实?在测试应用程序中编译的输出显示它被编译为一个简单的!= null检查。 - Darren Ruane
你说得对,那是一种假设,因为那是对象初始化语法。但即使在调试版本上,它也会编译成ldloc.0; ldnull; cgt.un,这意味着你是完全正确的,不会发生任何分配。我非常惊讶并为造成困惑而道歉。 - Joseph Lennox
如果编译成更简单的!= null,为什么会有所改进,考虑到!= null更易读? - EKW
@EKW 因为 != 运算符可以在类中进行重载(从而调用自定义实现),而上述代码使用了模式匹配,这将不会调用任何重载运算符。我现在无法检查,但我相信上述代码的编译输出将始终将对象转换为 object(确保不调用任何重载运算符)。 - Darren Ruane

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接