2012-03-06 38 views
5

私は、ワーカースレッド上で操作され、DataGridViewにバインドされたBindingSourceを持つメインUIスレッドのBindingSourceにバインドされたオブジェクトを含む非同期バインディングリストを持っています。BindingList内のオブジェクトの検索

リストを繰り返し処理することなく、BindingListにオブジェクトを配置することはできますか?

私はLINQのフードの下で見ましたが、基本的には砂糖でコーティングされたforeachループです。また、IBindingList.Find()を実装すれば、それはfor-loop以上のものではありません...

BindingListをミラーリングして辞書に同期/見つかったオブジェクトに渡して結果(インデックス)をBindingListに渡しますが、オブジェクトの追加と削除があまりにも多く、整理しておくことができないため、これは機能しません。

これは、株式市場のリアルタイムの高頻度データを扱う高性能アプリです。だから私はBindingListを反復することができません。それはあまりにも非効率的です。

誰かに助言や解決策を教えてもらえますか?

答えて

5

なので、ある種の高速検索バインディングリスト...これは私が先に準備したものです。

これはあなたが参照した「同期/マップ」アプローチです。私は、主なボトルネックがリスト内の項目を検索していたデータを高速にチェックするためにこれを使用しました。私は、同期しているか「組織化」しておく必要があるすべての方法をカバーしてきたと信じています。あなたはAddRangeのテストを追加するかもしれません - 私はデコンパイラを手にする必要はありません、私はそれが単にInsertItemを呼び出すかわかりません。

明らかに、メモリ使用量と挿入時間を増やすことで、2つのリストを維持することができますが、速くデータをチェックするためには、通常、ルックアップ時間を大幅に短縮できます。

BindingListのようにクラスを使用しますが、アイテムをすばやく検索する必要がある場合は、FastFindメソッドを使用します。

public class FastLookupBindingList<TKey, TVal> : BindingList<TVal> 
{ 
    private readonly IDictionary<TKey, TVal> _dict = new Dictionary<TKey, TVal>(); 
    private readonly Func<TVal, TKey> _keyFunc; 

    public FastLookupBindingList(Func<TVal, TKey> keyFunc) 
    { 
     _keyFunc = keyFunc; 
    } 

    public FastLookupBindingList(Func<TVal, TKey> keyFunc, IList<TVal> sourceList) : base(sourceList) 
    { 
     _keyFunc = keyFunc; 

     foreach (var item in sourceList) 
     { 
      var key = _keyFunc(item); 
      _dict.Add(key, item); 
     } 
    } 

    public TVal FastFind(TKey key) 
    { 
     TVal val; 
     _dict.TryGetValue(key, out val); 
     return val; 
    } 

    protected override void InsertItem(int index, TVal val) 
    { 
     _dict.Add(_keyFunc(val), val); 
     base.InsertItem(index, val); 
    } 

    protected override void SetItem(int index, TVal val) 
    { 
     var key = _keyFunc(val); 
     _dict[key] = val; 

     base.SetItem(index, val); 
    } 

    protected override void RemoveItem(int index) 
    { 
     var item = this[index]; 
     var key = _keyFunc(item); 
     _dict.Remove(key); 

     base.RemoveItem(index); 
    } 

    protected override void ClearItems() 
    { 
     _dict.Clear(); 
     base.ClearItems(); 
    } 
} 

使用法:

public class Person 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
    } 

    private void simpleButton1_Click(object sender, EventArgs e) 
    { 
     var keyedBindingList = new FastLookupBindingList<int, Person>(p => p.Id) 
            { 
             new Person {Id = 1, Name = "Joe"}, 
             new Person {Id = 2, Name = "Josephine"} 
            }; 
     var person = keyedBindingList.FastFind(2); 
     var unkonwn = keyedBindingList.FastFind(4); 
    } 
+0

THX stevenP!私はあなたの解決策を見たことがなかった。私はこれを実装しようとしています。それは良い見えて、これは私が以前にしようとしていたが、場所に同期を得ることができなかった –

+0

どのようにこのスレッドセーフを行いますか。 backgroundthreadがインデックス付きバインディングリストを更新するのと同じユースケースがあります。ただし、実装を使用すると、同期の問題が発生します。あらゆる場所でロックを追加すると、パフォーマンスも低下します。 – Rik

+0

ISynchronizeInvoke.Invokeを使用して更新を行うことはできますか?つまり、バックグラウンドワーカーを使用してデータの取得/操作の長期実行タスクを実行し、準備ができたらInvokeを呼び出してUIスレッドを取得し、BindingListを更新します。 –