2013-07-09 5 views
6

私がよく遭遇する問題は、オブジェクトのコレクションを、そのオブジェクトの一意の「インデックス」である特定のフィールド/プロパティによって取得できるように格納する必要があることです。たとえば、nameフィールドが一意の識別子であるPersonオブジェクトがあり、Personオブジェクトのコレクションからのname="Sax Russell"を取得できるようにしたいと考えています。 Javaでは通常Mapを使って実際にSetが必要ですが、マップのキーとしてオブジェクトの「インデックス」フィールドを常に使用しています。すなわち、peopleMap.add(myPerson.getName(), myPerson)です。私はこのように、Dictionary SとC#で同じことをやって考えていた:C#コレクションはプロパティによってインデックス付けされていますか?

class Person { 
    public string Name {get; set;} 
    public int Age {get; set;} 
    //... 
} 

Dictionary<string, Person> PersonProducerMethod() { 
    Dictionary<string, Person> people = new Dictionary<string, Person>(); 
    //somehow produce Person instances... 
    people.add(myPerson.Name, myPerson); 
    //... 
    return people; 
} 

void PersonConsumerMethod(Dictionary<string, Person> people, List<string> names) { 
    foreach(var name : names) { 
     person = people[name]; 
     //process person somehow... 
    } 
} 

しかし、これは不器用なようだ、とDictionaryのキーとその値の間にかなりの疎結合を紹介します。私は暗黙のうちに、それぞれPersonを格納するキーとしてNameプロパティを使用してPerson辞書のすべてのプロデューサに依存しています。 people["Sax Russell"]の要素が実際にPersonName="Sax Russell"であるという保証はありません。辞書にアクセスするたびに再確認しない限りです。

Personオブジェクトのコレクションがカスタム等価比較者またはLINQクエリを使用して名前でインデックス付けされることを明示的に確認する方法がいくつかありますか。ルックアップは一定時間滞在することが重要です。そのため、私はList.FindまたはEnumerable.Whereを使用できません。私はHashSetを使って試してみましたが、指定されたオブジェクトのNameフィールドだけを比較する等価比較器で構築しましたが、Personオブジェクトを名前だけで検索する方法はありません。

+1

はちょうどこのつまずいていますが、[KeyedCollection](httpsを検討しています: //msdn.microsoft.com/en-us/library/ms132438(v=vs.110).aspx)class? – ygoe

答えて

3

このタスクを実行するために、辞書を使用して独自のコレクションを構築することができます。アイデアは、Personを受け取り、Nameプロパティを読み取って文字列を返すデリゲートを格納することです。ここで

は、このようなコレクションの骨格ソリューションです。ここ

public class PropertyMap<K,V> : ICollection<V> { 
    private readonly IDictionary<K,V> dict = new Dictionary<K,V>(); 
    private readonly Func<V,K> key; 
    public PropertyMap(Func<V,K> key) { 
     this.key = key; 
    } 
    public void Add(V v) { 
     dict.Add(key(v)); 
    } 
    // Implement other methods of ICollection 
    public this[K k] { 
     get { return dict[k]; } 
     set { dict[k] = value; } 
    } 
} 

は、それを使用する方法である:

PropertyMap<string,Person> mp = new PropertyMap<string,Person>(
    p => p.Name 
); 
mp.Add(p1); 
mp.Add(p2); 
+0

私はこれが好きです。間違いなく、この問題に対する包括的な解決策です。 –

+0

両方の答えは良いですが、私はこれがどのように一般的であるか、C#の関数代理人が "このオブジェクトからキーを派生させる"という定義をカプセル化する方法を好んでいます。 – Edward

+0

あなたが今必要とするのは、Linqのオブジェクトが検索のためにインデックスを使用するように、Linqプロバイダです: 'mp.Where(person => person.Name ==" Bob ")。FirstOrDefault() ' –

6

組み込みのものがあれば分かりませんが、自分でキーを指定して辞書をラップしてからIList<Person>を実装することは何もありません。ここのキー(意図していない)は、消費者が基礎となる辞書にアクセスできないため、キーが正確であることを保証することができます。実装の

一部は、同様のカスタムインデクサを注意し、次のようになります。サイドボーナスとして

public partial class PersonCollection : IList<Person> 
{ 

    //the underlying dictionary 
    private Dictionary<string, Person> _dictionary; 

    public PersonCollection() 
    { 
     _dictionary = new Dictionary<string, Person>(); 
    } 

    public void Add(Person p) 
    { 
     _dictionary.Add(p.Name, p); 
    } 

    public Person this[string name] 
    { 
     get 
     { 
      return _dictionary[name]; 
     } 
    } 

} 

を、あなたはまた、かかるコードを変更することなく、後で実装を変更するのは自由です。

+0

それはまさに私が言うつもりだった。 :) –

関連する問題