2012-03-13 7 views
0

スレッドセーフな方法でコレクションを反復処理したいと思っています。私は一つの方法を持っているconvieniantです見つけるもっと使いやすい方法デリゲートをデリゲートするデリゲート

/// <summary> 
/// Saves each item in the list to the database 
/// </summary> 
protected static void SaveListToDatabase() { 
    this.VisitAllItems(item => { 
     bool itemSavedSuccessfully = item.SaveToDB(); 
     if(!itemSavedSuccessfully) { 
      //log error message 
     } 
    }); 
} 

それの使用の別の例は、

/// <summary> 
/// Get the number of special items in the list. 
/// </summary> 
protected int GetNumberOfUnsynchronisedItems() { 
    int numberOfSpecialItems = 0; 
    this.VisitAllItems((item) => { 
     numberOfSpecialItems += item.IsItemSpecial() ? 1 : 0; 
    }); 
    return numberOfSpecialItems; 
} 

だろう、それの例が使用だ

/// <summary> 
/// This visits all items and performs an action on them in a thread manner 
/// </summary> 
/// <param name="visitAction">The action to perform on the item</param> 
public void VisitAllItems(Action<Item> visitAction) { 
    lock (listLock) { 
     foreach (Item item in this.ItemList) { 
      visitAction.Invoke(item); 
     } 
    } 
} 

と呼ばれるのかもしれない。しかし、私はそこに確信していますAction<>デリゲートではなくFunc<>を使用してこの値を返すように、VisitAllItemsメソッドを書き込む方がよい方法です。私はいくつかのことを試しましたが、コンパイルエラーで終わります。

誰もこの方法を実装するためのより良い方法はありますか?

おかげで、 Alasdair

答えて

2

これは、すべての使用状況によって異なりますので、それは正確に何を伝えるのは難しいですあなたのために役立つでしょう。しかし、1つの方法は、コレクションの各アイテムに対して1つの結果を返し、LINQを使用して結果を結合することです。

public IEnumerable<TResult> VisitAllItems<TResult>(
    Func<Item, Result> visitfunction) 
{ 
    var result = new List<TResult>(); 
    lock (listLock) 
    { 
     foreach (Item item in ItemList) 
      result.Add(visitfunction(item)); 
    } 
    return result; 
} 

通常、私は手動でList<T>を作成するのではなく、yield returnを使用しますが、私はそれが理由lockの、この方法の方が良いと思います。

使い方は次のようになります:svickと@jonskeet両方に

protected int GetNumberOfUnsynchronisedItems() 
{ 
    return VisitAllItems(item => item.IsItemSpecial()) 
        .Count(isSpecial => isSpecial); 
} 
+0

感謝。私はあなたの説明と両方の両方が私の状況を考慮して同等に適していると理解しています。私がそうでないと確信できない限り、私は誰かがジョンに挑戦するよりも良い理由であなたにポイントを与えました。歓声 – ajmccall

+0

評判スコアに基づいて決めるのではなく、回答自体に基づいて決定するべきです。また、複数の回答をアップウィートすることで、 "ポイントを与える"ことができます。そしてあなたは、あなたが善良で有用なすべての答えをupvoteする必要があります。 – svick

+0

公正なポイント(http://blog.stackoverflow.com/2010/01/stack-overflow-where-we-hate-fun/)。しかし、私の受け入れられた答えはまだ立つ。 @JonSkeetにも+1してください。 – ajmccall

2

あなたはAggregateモデルに従うことができるが、使用するひどく快適ではないでしょう。

public TAccumulate VisitAllItems<TAccumulate>(TAccumulate seed, 
    Func<Item, TAccumulate, TAccumulate> visitor) { 
    TAccumulate current = seed; 
    lock (listLock) { 
     foreach (Item item in this.ItemList) { 
      current = visitor(current, item); 
     } 
    } 
    return current; 
} 

... 

protected int GetNumberOfUnsynchronisedItems() { 
    return VisitAllItems(0, 
     (count, item) => count + item.IsItemSpecial() ? 1 : 0); 
} 

をあなたが実際にどのように多くの異なる集計機能が必要なのでしょうか?たとえば、あなただけカウントしている時間のほとんど場合、あなたが必要になることがあります。その後、

public void VisitAndCount<TAccumulate>(Func<Item, bool> visitor) { 
    int count = 0; 
    lock (listLock) { 
     foreach (Item item in this.ItemList) { 
      if (visitor(item)) { 
       count++; 
      } 
     } 
    } 
    return count; 
} 

protected int GetNumberOfUnsynchronisedItems() { 
    return VisitAndCount(item => item.IsItemSpecial()); 
} 
関連する問題