【问题标题】:C# Code Simplification Query: The Null Container and the Foreach LoopC# 代码简化查询:Null 容器和 Foreach 循环
【发布时间】:2009-03-13 21:39:39
【问题描述】:

我经常有类似这样的代码:

if (itm != null)
{
    foreach (type x in itm.subItems())
    {
        //dostuff
    }
}
//do more stuff

在省略//do more stuff 的情况下,很容易避免额外的foreach 循环。通过使用适当的命令退出作用域(根据发生的情况,这通常意味着return 语句或continue statement)。

这类事情往往会导致箭头代码。我目前有几种方法可以解决这个问题:

  • 使用itm = itm == null ? itm.subItems() : emptyArray之类的代码
  • 允许箭头代码
  • 使用goto
  • 使用邪恶的作用域技巧(将整个事物、if 语句全部包装在一个作用域中,然后将其打破)。在我看来,邪恶的作用域黑客攻击基本上等同于goto,只是更丑更难阅读,所以我不认为这是一个有效的解决方案。
  • 将一些块重构为新方法。事实上,在少数情况下,这可能一个很好的解决方案,但大多数情况下它是不合适的,因为空引用主要是来自 MS 函数的错误条件。

有人愿意就哪些方法被认为更可取做出回应吗?

【问题讨论】:

    标签: c#


    【解决方案1】:

    如果你使用 C# 3,你总是可以写一个扩展方法:

    public static IEnumerable<SubItem> SafeSubItems(this ItemType item)
    {
         return item == null ? Enumerable.Empty<SubItem> : source.SubItems();
    }
    

    那就写吧:

    foreach (SubItem x in itm.SafeSubItems())
    {
        // do stuff
    }
    // do more stuff
    

    关键是扩展方法甚至可以“在”空引用上被调用。

    如果有一个“null-safe dereferencing”操作符会更好,所以我们可以这样写:

    // Not valid C# code!
    foreach (SubItem x in itm?.SubItems() ?? Enumerable.Empty<SubItem>())
    {
    }
    

    或者只是在IEnumerable&lt;T&gt; 上定义一个EmptyIfNull 扩展方法并使用

    // Not valid C# code!
    foreach (SubItem x in (itm?.SubItems()).EmptyIfNull())
    {
    }
    

    【讨论】:

    • 乔恩真是太棒了!我有一个扩展方法来返回一个列表或 null(如果列表为 null)以节省我的 if 检查,然后返回数据 => .ToListIfNotNullOrEmpty()。相同的概念:) 我要使用你的想法!谢谢大家!
    • 我喜欢这个解决方案。它减少了空值检查的重复,这意味着如果产生足够的空集合是值得的。就我而言,这可能是真的。很遗憾我目前正在使用 C# 2,但是一旦我更新,请记住这一点。
    • 如果需要,我想我现在可以将此解决方案与函数一起使用,而不是扩展方法。
    • 或者我可以安装 LINQ Preview (download.microsoft.com/download/4/7/0/…)。
    • 嗯。我向一位同事展示了这种技术,他的回答是调用可能为 null 的对象的扩展方法非常违反直觉,因此令人困惑。所以,我不认为这种技术会成为我的任何生产代码。
    【解决方案2】:

    您可以使用Coalesce 运算符(编码为双问号,??,.net 2 向上)。这将返回值列表中的第一个非空值,所以在这个 sn-p...

    MyClass o1 = null;
    MyClass o2 = new MyClass ();
    MyClass o3 = null;
    return o1 ?? o2 ?? o3;
    

    ...o2 将被返回。

    因此您可以将原始代码示例重新编码为

    foreach (type x in (itm ?? emptyArray).subItems())
    {
        //dostuff
    }
    
    //do more stuff
    

    但是,我个人不介意嵌套。立即清楚发生了什么。我发现 Coalesce 运算符有点难以阅读,而且为了清晰起见,这个小巢是一个很小的代价。

    【讨论】:

    • 问题不在于 itm.subItems() 是否为空,而是如果 itm 为空,在这种情况下这段代码就会崩溃。
    • @sgreeve 问题实际上是如何读取的,它? emptyArray 真的很奇怪,因为在 emptyArray 中你实际上需要一个 defaultForItmClassWithAnArray ...,这会让你为它的 emptyArray 获取一个 ItmClass 实例 - 我认为这不值得
    • @Freddy Rios - 请注意,我正在重构原始问题中发布的代码示例,其中包括“emptyArray”,并没有暗示这将逐字进行。
    【解决方案3】:

    我喜欢少嵌套,对我来说它读起来更好。请不要转到:)

    我保持方法简短,因此通常是该场景的回报。

    if (itm == null) return;
    foreach (type x in itm.subItems())
    {
       //dostuff
    }
    

    如果需要更多的东西,是简单的语句并且可以在foreach之前完成,你可以:

    if (itm == null)
    {
       //do more stuff 
       return;
    }
    foreach (type x in itm.subItems())
    {
       //dostuff
    }
    

    如果不是上述情况,则可能是方法太长,无论如何都会将其中的一部分移走。大概:

    if( itm != null ) SomeActionOnSubItems(itm.subItems);
    // do more stuff (can be some method calls depending on level of abstraction).
    

    【讨论】:

    • 您忽略了我示例中的“做更多事情”步骤。当我没有“做更多事情”时,您的解决方案已经被提及。
    • @Brian 它根据我们正在做的事情而有所不同,更新了更多场景,resharper 有助于超快速地移动它:)
    【解决方案4】:

    就个人而言,我可能会按照您的方式保留结构。

    第一个选项 (itm = itm == null ?itm.subItems() : emptyArray) 似乎比其他选项不那么讨厌,但我还是更喜欢你的原始选项。

    问题是,从另一个开发人员的角度来看,其他任何事情都会使您的代码变得不那么明显。如果有一个 foreach 贯穿一个集合,我希望该集合(至少通常)包含其中的项目。如果集合可能是空的,那么对于没有 cmets 的其他人来说,这将是不明显的(这比 if 检查花费的时间更长)。

    为了避免 if 检查而采取任何技巧似乎是你太聪明了。

    【讨论】:

    • 我的问题是,在某些情况下,由于所有 if 语句,代码变得难以阅读。当然,其他开发人员可能无法阅读它。但事实上,即使是 也无法阅读它,更不用说其他开发人员了。
    • 空集合不是问题,只要集合本身为空。我通常尝试将我的集合实例化为一个空集合来解决此类问题(然后您不需要空检查),但是在使用其他库时这并不总是可行的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-29
    • 2012-07-28
    • 1970-01-01
    • 2014-05-14
    • 2018-10-04
    相关资源
    最近更新 更多