代码之家  ›  专栏  ›  技术社区  ›  Brian

C#代码简化查询:空容器和Foreach循环

c#
  •  12
  • Brian  · 技术社区  · 17 年前

    我经常有这样的代码:

    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语句全部包装在一个范围内,然后将其打破)。在我看来,邪恶的范围界定黑客基本上等同于 后藤 除了丑陋和更难阅读,所以我不认为这是一个有效的解决方案。
    • 将一些块重构为新方法。事实上,在一些情况下,这可能 这是一个很好的解决方案,但通常并不合适,因为空引用主要是来自MS函数的错误条件。

    4 回复  |  直到 17 年前
        1
  •  15
  •   Jon Skeet    17 年前

    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
    

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

    最好是一个“空安全解引用”操作符,这样我们就可以写:

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

    或者只是定义一个 EmptyIfNull 扩展方法 IEnumerable<T> 和使用

    // Not valid C# code!
    foreach (SubItem x in (itm?.SubItems()).EmptyIfNull())
    {
    }
    
        2
  •  3
  •   razlebe    17 年前

    你可以使用 Coalesce

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

    …氧气将被返回。

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

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

    然而,我个人并不介意筑巢。发生了什么事马上就清楚了。我发现Coalesce操作符更难理解,而这个小嵌套对于清晰性来说是一个很小的代价。

        3
  •  2
  •   eglasius    17 年前

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

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

    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).
    
        4
  •  0
  •   Reed Copsey    17 年前

    就我个人而言,我可能会离开你的结构。

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

    问题是,从另一个开发人员的角度来看,其他任何东西都会使代码不那么明显。如果有一个foreach在一个集合中运行,我希望该集合(至少在正常情况下)中包含项。如果集合可以是空的,那么对于没有注释的其他人来说,这是不明显的(这比If检查花费的时间更长)。

    做任何一个黑客来避免if检查似乎你太聪明了。