簡體   English   中英

標記空集合,或者如果集合不為空(C#)枚舉成員

[英]Flag empty collection or enumerate over members if collection is not empty (C#)

(對我而言)顯而易見的解決方案如下:

if (widgets.Count == 0)
{
    //Handle empty collection
}
else 
{
    // Handle non-empty collection
    foreach (widget in widgets)
    {
        // Process widget
    }
}

這似乎很固定。 我很想用“ else foreach”消除一定程度的縮進,但是我能聽到腦中低沉的尖叫聲,但是當我想到時會立即開始:

if (widgets.Count == 0)
{
    //Handle empty collection
}
else foreach (widget in widgets)
{
    // Process widget
}

還有其他想法或建議嗎?

您似乎關心代碼的可讀性和可維護性。 這樣的代碼塊:

if (someCondition)
{
    // do
    // a
    // lot      
    {
        // moar
        // nesting
    }
}
else
{
    // do 
    // something
    // else
    {
        // possibly with even more nesting
    }
}

通常被認為既不可讀也不可維護。 當然,您可以濫用語法或使用其他技巧來減少嵌套的水平 ,但是嵌套太多的事實應該是一種方法無法勝任的事實。

至少將代碼塊提取到自己的方法中,從而自動減少嵌套。

如果首先將上述代碼放在自己的方法中,則可以從if()返回,完全刪除else塊。

public void DoSomethingWithWidgets(ICollection<Widget> widgets)
{
    if (widgets.Count == 0)
    {
        HandleEmptyCollection();
        return;
    }

    foreach (widget in widgets)
    {
        ProcessWidget(widget);
    }
}

或者改為將代碼完全放在自己的類中,然后讓一個類自行確定它是否適用於其輸入。 您可以這樣實現:

public interface IVisitor
{
    bool CanVisit(ICollection<Widget> widgets);

    void Visit(ICollection<Widget> widgets);
}

public class EmptyCollectionVisitor : IVisitor
{
    public bool CanVisit(ICollection<Widget> widgets)
    {
        return widgets.Count == 0;
    }

    public void Visit(ICollection<Widget> widgets)
    {
        // Handle empty collection
    }
}

public class NotEmptyCollectionVisitor : IVisitor
{
    public bool CanVisit(ICollection<Widget> widgets)
    {
        return widgets.Count > 0;
    }

    public void Visit(ICollection<Widget> widgets)
    {
        foreach (var widget in widgets)
        {
            // Process widget
        }
    }
}

然后,如果可以的話,讓所有訪問者訪問集合,然后完成。 附加的好處是,類本身可以更好地測試和重用。

然后,通過遍歷已注冊的訪問者來利用它:

var yourInputCollection = { either a list that is empty or not };

IVisitor[] collectionVisitors = new[] { 
    new EmptyCollectionVisitor(), 
    new NotEmptyCollectionVisitor() 
};

foreach (var visitor in collectionVisitors)
{
    if (visitor.CanVisit(yourInputCollection))
    {
        visitor.Visit(yourInputCollection);
    }
}

是的,這似乎有些矯kill過正,但是濫用語法以在盡可能少的水平空間容納盡可能多的代碼,在我看來,其優雅程度甚至更低。

在這種情況下,我很想使用您所說的模式:

if (myVar.Value < 1)
{
    //...
}
else switch (myVar.Value)
{
    case 1:
        //...
        break;
    case 2:
        //...
        break;
}

問題是Visual Studio自動縮進將不斷更改它,並在else語句后縮進代碼。

我的建議是使用您問題中的第一個例子。 消除else塊對可讀性/可維護性幾乎沒有幫助。

世界上存在比縮進級別更糟糕的事情,因此,我絕對更希望前者比后者更不尋常。

另一種方法是刪除foreach的語法糖:

using (var en = widgets.GetEnumerator())
{
  if (en.MoveNext())
  {
    do
    {
      var widget = en.Current;
      // process widget.
    } while (en.MoveNext());
  }
  else
  {
    // Handle empty.
  }
}

從語法上講,它更糟,但是確實具有一些優點。

  1. 現在,您可以將其與任何IEnumerable<Widget>一起使用,而不必堅持使用ICollection<Widget> ,這意味着您可以跳過內存中的創建集合,以便查看計數。

  2. 這是稍微更高效,我們跳過Count的呼叫和分支的時候,我們要(隱藏在foreach )發現,如果在第一個電話是空MoveNext()的任何方式。

當您需要對第一項執行不同的操作時(例如,從第一項創建累加器,然后累加操作將基於其余項進行更改),類似的方法非常有用。

就是說,除非真正走上正軌,要么能夠使用所有可枚舉對象的能力是真正的收獲,我只想將問題中的第一種情況視為最常規的方法。 如果縮進確實很糟糕(由於縮進的代碼太大,很難閱讀),您可以隨時將其分解為其他方法。

在您的特殊情況下,我可能會選擇一個輔助變量來跟蹤枚舉是否為空。

bool empty = true;

foreach (widget in widgets) {
  empty = false;

  // Process widget
}

if (empty) {
  // Handle empty collection
}

這具有與喬恩·漢納(Jon Hanna)的答案相同的優勢,即無需枚舉兩次即可處理任何IEnumerable<Widget> ,但是在我看來,這是以可讀性更高的形式,並且自然避免了對一級塊的需求。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM