简体   繁体   English

调用方法时处理null对象

[英]Handling null objects when calling methods

I have been looking around finding the best possible option for handling null objects in calling a method (or method chain). 我一直在寻找在调用方法(或方法链)时处理空对象的最佳选择。

It is our common practice to check with if condition: 我们的常见做法是检查条件:

if ( customObject != null ) {
    customObject.callMe();
}

Which can be further improved by using extension methods: 使用扩展方法可以进一步改进:

Program customObject = null;
if (customObject.NotNull()) {
    customObject.CallMe();
}

public static bool NotNull(this object o) {
    return o == null;
}

PLEASE NOTE: I normally ignore ! 请注意:我通常会忽略! from my programming practice. 从我的编程实践。 Hence it is wise to say that for me extension methods are good to go. 因此,明智地说对我来说扩展方法很好。

However, it becomes very complicated in dealing with when the Method chain is involved. 但是,在处理涉及Method链的时候,它变得非常复杂。

customObject.CallMe().CallMe2() ex... 

How you do think it can be handled in C#, so that the CallMe is called only if customObject is not null and CallMe2 is called only if CallMe returns non null object. 你如何看待它可以在C#进行处理,这样CallMe只调用如果customObject不是null CallMe2被称为只有当CallMe返回非空对象。

Of course I can use If conditions or ternary operator. 当然我可以使用If条件或三元运算符。 However, I would want to know if vNext, C#5.0 has some thing to offer. 但是,我想知道vNext,C#5.0是否有一些东西可以提供。

In the upcoming C# 6 (vNext) has the ?. 在即将到来的C#6(vNext)中有?. operator (Null Conditional Operator) which easily allow you to chain null reference checks for every nested property. 运算符(Null条件运算符),它允许您为每个嵌套属性链接空引用检查。

An example of this would be: 一个例子是:

int? first = customers?.[0].Orders?.Count();

This was a requested feature in the Visual Studio UserVoice site 这是Visual Studio UserVoice站点中的请求功能

Add ?. 加? Operator to C# C#的运算符

You can see the status for all the new language features for C# 6.0 on the Codeplex site for Roslyn: 您可以在Roslyn的Codeplex站点上查看C#6.0的所有新语言功能的状态:

C# 6 Language Features Status C#6语言功能状态

You can currently write these kinds of extension methods: 您目前可以编写以下类型的扩展方法:

public static class Extensions
{
    public static R IfNotNull<T, R>(this T @this, Func<T, R> @select) where T : class
    {
        return @this.IfNotNull(@select, () => default(R));
    }

    public static R IfNotNull<T, R>(this T @this, Func<T, R> @select, Func<R> @default) where T : class
    {
        return @this != null ? @select(@this) : @default();
    }

    public static void IfNotNull<T>(this T @this, Action<T> @action) where T : class
    {
        if (@this != null)
        {
            @action(@this);
        }
    }
}

Then call them like so: 然后像这样打电话给他们:

customObject 
    .IfNotNull(y => y.CallMe())
    .IfNotNull(y => y.CallMe2());

C# 5 has nothing to offer to simplify your code. C#5没有提供简化代码的功能。 You are stuck with either: 你坚持使用:

  • Nested if conditions 如果条件嵌套
  • Complicated ternary expressions 复杂的三元表达式
  • Englobing try/catch catching NullReferenceException (you really shouldn't do that if a null in your chain is not really exceptionnal, ie it should not happen, but you want a keepsafe). Englobing try / catch捕获NullReferenceException(如果你的链中的null不是真的异常,你真的不应该这样做,即它不应该发生,但你想要一个keepsafe)。

However, the future may be brighter, as the null-propagating operator ?. 但是,作为零传播算子,未来可能会更加明亮? is a proposed feature of C# 6, and has already be implemented is Roslyn , so it would be a great surprise if it was not effectively in C# 6. With this operator, you will be able to write 是C#6的一个提议特性,并且已经实现了Roslyn ,所以如果在C#6中没有有效的话,那将是一个很大的惊喜。通过这个运算符,你将能够编写

customObject?.CallMe()?.CallMe2()?.[...] 

And the compiler will create the nested if for you (and even ensure that every method is only called once). 编译器将为您创建嵌套if(甚至确保每个方法只调用一次)。 So, A little more patience... 所以,多一点耐心......

C# 6 does have the ? C#6确实有? operator, otherwise you can look into monads, particularly the Maybe monad , eg here , here , elsewhere . 运算符,否则你可以查看monad,尤其是Maybe monad ,例如这里这里其他地方 Is it worth using monads in a non-functional language that wasn't designed with them in mind is a different question. 是否值得使用非功能性语言中的monad是一个不同的问题。

Since people are tearing apart the answer that assumes you aren't an idiot, here is one that assumes you are. 由于人们正在撕裂假设你不是白痴的答案,这里假设你是一个人。

You should have checked when customObject was created, because that is the point at which you would have know why you got a null . 您应该在创建customObject进行检查, 因为这是您可以知道为何获得null的点 If you didn't check then, your best bet now is to let your code throw the exception. 如果你没有检查,那么现在最好的办法是让你的代码抛出异常。 If there is still something you can do that makes perfect sense without having the object you clearly need, you can fix that up in the catch of the exception, but there probably isn't. 如果仍然有一些你可以做的事情,那么在没有你明确需要的对象的情况下完全有意义,你可以在异常的捕获中修复它,但可能没有。

All of the mechanisms that ignore the significance of an unexpected null are just passing along the failure to have paid attention earlier, including habitual overuse of the proposed null-propagation operator, and all habits of checking objects before use. 所有忽略意外null的重要性的机制只是传递失败而早先引起注意,包括习惯性过度使用所提出的零传播算子,以及在使用前检查对象的所有习惯。 They are seeking an efficient response to a catastrophe that minimizes attention paid to it. 他们正在寻求对灾难的有效回应,以最大限度地减少对它的关注。

If you are signalling important information with the nullness of an object, that is probably a bad design choice, and you should have converted the null into more usable information when it was introduced. 如果您使用对象的null来表示重要信息,那么这可能是一个糟糕的设计选择,并且您应该在引入时将null转换为更有用的信息。

Testing for the unexpected nullness of an object right before it is used, rather than right after it is produced cannot really help anyone. 在使用对象之前测试对象的意外无效,而不是在对象产生之后对其进行测试,这对任何人都无法真正帮助。 It is a code-smell that indicates you have misplaced responsibility, probably in one of these ways: 这是一种代码嗅觉,表明你有错误的责任,可能是以下列方式之一:

  1. You are unwilling to challenge people who make awkward design decisions without adequate explanation 如果没有充分的解释,你不愿意挑战做出尴尬设计决策的人
  2. You do not feel you have control over something you have incorporated but are too lazy to wrap it 你不觉得你已经控制了你已经合并的东西但是懒得把它包起来
  3. You don't believe nulls should exist, and you are trying to ignore them, so you haven't thought through any policy on them. 你不相信空值应该存在,并且你试图忽略它们,所以你没有考虑过它们的任何政策。

The policy you indicate, of eating the null and continuing execution without analyzing it, is a bad one. 你指出的政策,即在不分析的情况下吃零和继续执行,这是一个糟糕的政策。 If you intend to keep it, that is my other answer. 如果你打算保留它,这是我的另一个答案。 You should instead do something reasonable. 你应该做一些合理的事情。

In a lot of workplaces, putting an empty catch block anywhere is a clear documentation of future work. 在许多工作场所,将空挡块放在任何地方都是对未来工作的清晰记录。 So it is doing something. 所以它正在做点什么。 But leaving it there is never an option that should stand in good code. 但是留下它永远不会有一个应该保持良好代码的选项。 Such a block would more reasonably convert the exception into a response that creates future work to fix the source of the error, even if the code-block implements a workaround that addresses the problem in a more local way. 即使代码块实现了以更本地方式解决问题的解决方法,这样的块也会更合理地将异常转换为创建未来修复错误源的工作的响应。

This is the point of exception handling. 这是异常处理的重点。 If you want to respond to unusual circumstances like getting an unexpected null returned from a function, then catch the NullReferenceException , and call your recovery code from there. 如果要响应异常情况,例如从函数返回意外的null ,则捕获 NullReferenceException ,并从那里调用恢复代码。 The exception itself will stop further calls, so if that is all you wish to accomplish, implement no recovery code. 异常本身将停止进一步调用,因此如果您希望完成所有操作,请不要执行任何恢复代码。 I do not suggest this is a reasonable response, something like this should generally at least be logged. 我不建议这是一个合理的回应,这样的事情通常至少应该被记录下来。

try
{
    customObject.CallMe().CallMe2()
}
catch (NullReferenceException ex)
{
   /* save enough details to determine why you got an unexpected null */
}

Do not use exception handling to accomplish normal programming tasks, but do not avoid it excessively, by checking for every possible contingency, no matter how rare. 不要使用异常处理来完成正常的编程任务,但不要过度避免,通过检查每个可能的意外事件,无论多么罕见。 Decide what failures are likely in your code (that you can do anything about) and include a healthy list of catch 'es at the end of each reasonable-sized block. 确定代码中可能出现的故障(您可以做任何事情)并在每个合理大小的块的末尾包含一个健康的catch列表。

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

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