C# 调用方法时处理空对象

C# 调用方法时处理空对象,c#,c#-5.0,null-conditional-operator,C#,C# 5.0,Null Conditional Operator,我一直在寻找在调用方法(或方法链)时处理空对象的最佳选择 我们的常见做法是检查以下情况: if ( customObject != null ) { customObject.callMe(); } 可通过使用扩展方法进一步改进: Program customObject = null; if (customObject.NotNull()) { customObject.CallMe(); } public static bool NotNull(this object o

我一直在寻找在调用方法(或方法链)时处理空对象的最佳选择

我们的常见做法是检查以下情况:

if ( customObject != null ) {
    customObject.callMe();
}
可通过使用扩展方法进一步改进:

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

public static bool NotNull(this object o) {
    return o == null;
}
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);
        }
    }
}
请注意:我通常忽略!从我的编程实践中。因此,明智的说法是,对我来说,扩展方法很好

然而,当涉及方法链时,处理变得非常复杂

customObject.CallMe().CallMe2() ex... 
您认为如何在C#中处理它,以便仅当
customObject
不为null时调用
CallMe
,并且仅当
CallMe
返回非null对象时调用
CallMe2


当然,我可以使用If条件或三元运算符。但是,我想知道vNext,C#5.0是否提供了一些功能。

这是异常处理的要点。如果您希望对异常情况做出响应,例如从函数返回意外的null,则捕获NullReferenceException,并从那里调用恢复代码。异常本身将停止进一步的调用,因此如果您只想完成这些,请不要实现恢复代码我不认为这是一个合理的回答,通常至少应该记录类似的内容。

try
{
    customObject.CallMe().CallMe2()
}
catch (NullReferenceException ex)
{
   /* save enough details to determine why you got an unexpected null */
}
不要使用异常处理来完成正常的编程任务,但不要通过检查每一个可能的意外事件(无论多么罕见)来过度避免它。确定代码中可能出现的故障(您可以采取任何措施),并在每个合理大小的块末尾包含一个健康的捕获列表。

在即将到来的C#6(vNext)中有
?。
运算符(空条件运算符)这使您可以轻松地为每个嵌套属性链接空引用检查

这方面的一个例子是:

int? first = customers?.[0].Orders?.Count();
这是Visual Studio UserVoice站点中请求的功能

您可以在Roslyn的Codeplex网站上看到C#6.0的所有新语言功能的状态:

C#5对简化代码没有任何帮助。您必须遵守以下任一规定:

  • 嵌套if条件
  • 复杂三元表达式
  • Englobing try/catch捕获NullReferenceException(如果链中的null不是真正的exceptionnal,即它不应该发生,但您需要一个keepsafe,那么您确实不应该这样做)
然而,随着零传播算子的出现,未来可能会更加光明?。是C#6的一个建议特性,并且已经存在,因此如果它在C#6中不起作用,那将是一个巨大的惊喜。使用此运算符,您将能够写入

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

编译器将为您创建嵌套的if(甚至确保每个方法只调用一次)。所以,再耐心一点…

C#6确实有
操作符,否则您可以查看单子,尤其是单子,例如。在非功能性语言中使用Monad是否值得,而在设计时没有考虑Monad是另一个问题。

您目前可以编写以下几种扩展方法:

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

public static bool NotNull(this object o) {
    return o == null;
}
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);
        }
    }
}

由于人们正在撕开假设你不是白痴的答案,这里有一个假设你是白痴的答案

您应该在创建
customObject
时进行检查,因为在这一点上您会知道为什么会得到空值。如果您当时没有检查,那么现在最好的办法就是让您的代码抛出异常。如果在没有明确需要的对象的情况下,您仍然可以做一些非常有意义的事情,那么您可以在异常的catch中修复它,但可能没有

所有忽略意外空值重要性的机制都只是传递了之前没有注意到的错误,包括习惯性地过度使用提议的空值传播操作符,以及使用前检查对象的所有习惯。他们正在寻求对一场灾难的有效反应,尽量减少对它的关注

如果您用对象的空值来表示重要信息,那么这可能是一个错误的设计选择,并且在引入空值时,您应该将其转换为更有用的信息

在使用对象之前而不是在生成对象之后测试对象的意外空值并不能真正帮助任何人。这是一种代码气味,表明您承担了错误的责任,可能是以下方式之一:

  • 你不愿意挑战那些在没有充分解释的情况下做出笨拙设计决策的人
  • 你觉得你无法控制你已经合并的东西,但却懒得去包装它
  • 你不相信空值应该存在,你试图忽略它们,所以你没有考虑过任何关于它们的政策
  • 您所指出的策略,即吃空并继续执行而不分析它,是一个糟糕的策略。如果你想保留它,那是我的另一个答案。你应该做一些合理的事情


    在许多工作场所,将空的捕获块放在任何地方都是对未来工作的清晰记录。因此,它正在做一些事情。但离开它,就再也没有一个选项应该存在于好的代码中。这样的块将更合理地将异常转换为一个响应,该响应将创建修复错误源的未来工作,即使代码块实现了一个以更本地的方式解决问题的解决方法。

    以哪种方式,使用NotNull()这是一种改进吗?通过让扩展方法与实际情况配合使用,我可以控制我定义为Null的内容,因此这是一种改进。(我通常忽略!从我的编程实践中)。您可能希望至少查看一下(myObject!=null)是否是清晰的代码。我不确定使用模式来避免这种代码是否会对下一个程序员有所帮助