简体   繁体   English

C# - “对象引用未设置为对象的实例”

[英]C# - “Object reference not set to an instance of an object”

I'm currently trying to check if a reference is null before it is used by 'MyMethod' with: 我正在尝试检查引用是否为空,然后'MyMethod'使用它:

if (School.ClassRoom.Pupil.Age != null)
        {
            MyMethod(School.ClassRoom.Pupil.Age);
        }

However, I'm still getting the "Object reference not set to an instance of an object" on the first line because not only is the Age null, but also the Pupil and ClassRoom are sometimes null too. 但是,我仍然在第一行得到“对象引用未设置为对象的实例”,因为不仅Age为null,而且Pupil和ClassRoom有时也为null。

I'm getting the same problems using Try, Catch, Finally, since I get the same error in the Try piece of code. 我使用Try,Catch,最后得到了相同的问题,因为我在Try代码中遇到了同样的错误。

I don't want to have to check each ClassRoom for null, and then each Pupil for null, and then each Age for null each time I want to use this method. 我不想要检查每个ClassRoom为null,然后每个Pupil为null,然后每次Age为null,每次我想使用此方法。

Is there an easier way to do this? 有更简单的方法吗?

Note: the answer below was written in 2010, long before the null conditional operator was introduced in C# 6. 注意:下面的答案是在2010年编写的,早在C#6中引入了null条件运算符之前。


It sounds like you're after something like Groovy's null-safe dereferencing operator, which would let you write if (School?.ClassRoom?.Pupil?.Age != null) - but C# didn't have such a thing before C# 6. 听起来你像是在使用Groovy的null-safe dereferencing运算符,它会让你写if (School?.ClassRoom?.Pupil?.Age != null) - 但C#在C#6之前没有这样的东西。

I'm afraid you have to check each property for nullity, assuming it can be null: 我担心你必须检查每个属性是否 null,假设它可以 null:

if (School != null && School.ClassRoom != null && School.ClassRoom.Pupil != null
    && School.ClassRoom.Pupil.Age != null)
{
    MyMethod(School.ClassRoom.Pupil.Age);
}

Of course, you could put this whole if block including the method call itself in a helper method, and just call that. 当然,你可以将整个if包括方法调用本身的块放在一个帮助器方法中,然后调用它。

That's assuming it's valid for each property to be null to start with. 这是假设每个属性的有效开始时 null。 If you are able to design your classes so that null values aren't even allowed - and you validate this in contsructors etc - your code is likely to end up being a lot cleaner. 如果你能够设计你的类以便甚至不允许空值 - 并且你在contsructors等中验证它 - 你的代码可能最终变得更加清晰。

It's worth noting that there are two alternative approaches here - the one put forward by Chris in another answer is to create a "default" object for each property; 值得注意的是,这里有两种替代方法 - 克里斯在另一个答案中提出的方法是为每个属性创建一个“默认”对象; I usually find that it's better to always require a "real" value to be provided in a constructor. 通常发现总是要求在构造函数中提供“真实”值更好。 Default objects without real data can end up causing bugs which are harder to track down than NullReferenceException problems, as you can happily go along with the "dummy" data for a long time, and just get the wrong result at the end. 没有实际数据的默认对象最终会导致比NullReferenceException问题更难跟踪的错误,因为您可以很长时间地使用“虚拟”数据,并在最后得到错误的结果。 There are definitely times where it is the right thing to do, however - particularly when it comes to collections. 然而,肯定有时候这正确的事情 - 特别是在收藏方面。 It depends on the situation. 这取决于实际情况。

EDIT: Saeed has suggested an extension method in the comments. 编辑:Saeed在评论中提出了一种扩展方法。 I assume this would be something like: 我假设这将是这样的:

public static int? PupilAgeOrNull(this School school)
{
    return school != null && 
           school.ClassRoom != null && 
           school.ClassRoom.Pupil != null
           ? school.ClassRoom.Pupil.Age : null;
}

(Adjust for types appropriately.) (适当调整类型。)

I definitely prefer the idea of trying to keep things non-null elsewhere, but this will do it if you need it. 我绝对更喜欢尝试在其他地方保持非null值的想法,但如果你需要它会这样做。 It feels wrong to me though. 但我感觉不对。 At the heart of this gut feeling is the fact that you're navigating three or four properties to start with - this feels like a violation of the Law of Demeter to me. 这种直觉的核心是你开始使用三到四个属性 - 这对我来说就像违反了得墨忒耳法 Now I'm not one to get dogmatic about such things, but putting an extension method on School feels far too specific to me, for such a long path of properties. 现在我不是一个会对这些事情发表教条的人,但是对于我这样一条长长的道路来说, School放一个延伸方法我来说太过具体了。

Another alternative - which is also somewhat nasty, IMO - is to write three different extension methods: 另一种选择 - IMO也有些讨厌 - 是编写三种不同的扩展方法:

public static ClassRoom ClassRoomOrNull(this School school)
{
    return school == null ? null : school.ClassRoom;
}

public static Pupil PupilOrNull(this ClassRoom classRoom)
{
    return classRoom == null ? null : classRoom.Pupil;
}

public static int? AgeOrNull(this Pupil pupil)
{
    return pupil == null ? null : pupil.Age;
}

Then you can write: 然后你可以写:

int? age = School.ClassRoomOrNull().PupilOrNull().AgeOrNull();
if (age != null)
{
    MyMethod(age);
}

This means that the extension method on School isn't nearly so specific. 这意味着School的扩展方法并不是那么具体。 You've still got a long chain of method calls, and I'd still try to redesign to avoid this situation if possible, but at least there isn't quite such a tight tie from School to School.ClassRoom.Pupil.Age . 你仍然有很长的方法调用,我仍然会尝试重新设计以避免这种情况,如果可能的话,但至少从SchoolSchool没有这么紧密的关系School.ClassRoom.Pupil.Age

Here is a nice and elegant solution with Expression Trees . Expression Trees的一个漂亮而优雅的解决方案。 Try it and enjoy! 试试看吧!

Give the code you have shown, there is not an easier way. 给出你展示的代码,没有更简单的方法。 You will need to check each component. 您需要检查每个组件。

if (School != null && School.ClassRoom != null 
  && School.ClassRoom.Pupil != null 
  && School.ClassRoom.Pupil.Age != null) 
{
  ...
}

However, you can write your code in such a way that the members are never null . 但是,您可以编写代码,使成员永远不为null That way you can avoidn having to check for null . 这样你就可以避免检查null For example 例如

class School
{
  private ClassRoom _classRoom = new ClassRoom();

  public ClassRoom ClassRoom 
  {
    get {return _classRoom;}
  }
}

This will give School an empty class room to start with, so it is not null and cannot be set to null outside of the class since the property does not have a setter. 这将为School提供一个空的课堂空间,因此它不是null ,因为该属性没有setter,所以不能在类之外设置为null You can carry this concept forward, your list of pupils (I assume this would be a list) can be an empty list rather than a null instance etc. 你可以向前推进这个概念,你的学生列表(我假设这将是一个列表)可以是一个空列表而不是一个空实例等。

"Null-Object Pattern" comes to your rescue. “空对象模式”来拯救你。 Read here . 在这里阅读

So, you can have NullSchool, NullClassRoom, NullPupil, NullAge. 所以,你可以有NullSchool,NullClassRoom,NullPupil,NullAge。

Then you never need to check for null thing, instead you can have just one check (or method, like IsValid() in Age class, ofcourse virtual) in MyMethod to reject a age if it is not valid. 然后你永远不需要检查null的东西,而是你可以在MyMethod中只有一个检查(或方法,如Age类中的IsValid(),ofcourse virtual)以拒绝年龄,如果它无效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM