2012-01-27 4 views
0

私は同じケースでキーで値を見ることができるようにしたい、そして他のケースではコレクションを通して反復することができます。KeyValueではなくIEnumerable <ValueType>のIDictionary?

は、以前私はこれをやっていた:

public class Company 
{ 
    public string Name { get; set; } 
    public ICollection<Department> Departments { get; set; } 

} 

public class Department 
{ 
    public string Name  { get; set; } //display name 
    public string UniqueName { get; set; } //like a code value 
    public bool IsSelected { get; set; } 
} 

foreach(var d in someCompany.Departments) ... 

var departments = someCompany.Departments.Select(d => some projection stuff ... 

今、私はそれを効率的にためて、私はまた、固有名のために一致する文字列で、チェックボックスリストのようないくつかの他のコレクションを反復処理する必要があるいくつかの場所を持っている私が変更DepartmentsをIDictionaryに宣言しますが、これにより他のユースケースがより煩雑になり、常にValueまたはValuesにドリルダウンする必要があります。だから、DepartmentsプロパティはもはやDepartmentクラスのコレクションではなく、代わりにKeyValueペアのコレクションです。

foreach(ListItem item in someCheckBoxes.Items) 
{ 
    someCompany.Departments[item.Value].Selected = true; 
} 

foreach(var d in someCompany.Departments.Values) ... 

var departments = someCompany.Departments.Values.Select(d => some projection stuff ... 

また、私はこれらのリストを初期化するたびにです。KeyValueをIDictionaryのにリストを変換したり、追加することのために気にしません。

理想的には、ICollectionのように動作するコレクションがありますが、インデックス演算子もあり、辞書に内部的にアクセスする関数が含まれています。

OR私はそうのような2つのプロパティを持っているだろう、同期している滞在:

public Company(string uniqueName, string name, ICollection<Department> departments) 
{ 
    Name = name; 
    UniqueName = uniqueName; 
    DepartmentsByUniqueName = departments.ToDictionary<Department, string>(p => p.UniqueName); 
} 

public IDictionary<string,Department> DepartmentsByUniqueName { get; set;} 

public ICollection<Department> Departments { get { return DepartmentsByUniqueName.Values; } } 

public void AddDepartment(Department department) 
{ 
    DepartmentsByUniqueName.Add(department,department.UniqueName) 
} 

ここでの問題は、誰かが部門プロパティを使用して値のコレクションを取得し、追加/それにアイテムを削除し、reallizingことができなかったということです実際には辞書に追加する必要があります(setを実装すると、getを使ってcllectionを取得してアイテムを追加できるため、setを実装することはできません)。

あまりにも多くのコードを再作成するのではなく、 Dictionaryから自動的にIEnumerableを継承します。インターフェイスを明示的に実装しても、ユーザーがそのベースインターフェイスにキャストした場合に問題が発生する可能性があります。

本質的にIEnumerableを実装するクラスが必要ですが、効率のために内部的に辞書を利用するContainsと[]演算子もあります。

また、Departmentsコレクションと同期している追加DepartmentByUniqueName辞書プロパティを作成することもできます。

+1

理由だけではなく、辞書の 'Values'プロパティを使用していませんか? –

+0

プロパティはDepartmentKeyValuePairsではなくDepartmentsと呼ばれるためです。実際に辞書が単なるインデックスである場合は、あらゆるユースケースで常に.Valueまたは.Valuesにアクセスする意味がほとんどありません。 .Add。にキーを常に渡す必要はありません。対照的に、KeyedCollectionは素晴らしいです。なぜなら、アイテムを追加するたびにKeyのこのプロパティを使用するというコレクションにそのロジックを書き込むからです。 ICollection ではなく、ICollection です。 'KeyedCollection'の提案に対しては – AaronLS

答えて

3

KeyedCollectionをサブクラス化し、したがって、それを定義することです:

public class DepartmentCollection : KeyedCollection<String, Department> { 
    protected override String GetKeyForItem(Department item) 
    { 
     // EDIT: For your use case, this should work 
     return item.UniqueName; 
    } 
} 

や部門プロパティとしてあなたの会社のクラスでそれを使用します。

public class Company 
{ 
    public string Name { get; set; } 
    public DepartmentCollection Departments { get; set; } 
} 

KeyedCollection名前またはインデックスのいずれかで使用できます。

var departments = new DepartmentCollection(); 
departments.Add(new Department(...)); 
var accounting = departments["Accounting"]; 
foreach (var department in departments) { .... } 
var accountingExists = departments.Contains("accounting"); 
// etc 
+0

+1です。私はO(1)インデックスルックアップ(非リスト構造の場合はO(n)である 'Enumerable.ElementAt'とは対照的に)を使用できるので、私の回答はこれよりも好きです。 – Douglas

+0

私は通常、 ToListまたはToDictionaryが通常含まれます。 IEnumerableまたはICollectionをKeyedCollectionに変換する最良の方法は何ですか? – AaronLS

+0

'DepartmentCollection'を作成してから、IEnumerableまたはICollectionをforeachし、各項目を個別に追加する必要があります。 KeyedCollectionには、AddAllメソッドがありません。あるいは、 'DepartmentCollection'にコンストラクタオーバーロードを追加して、' IEnumerable 'をとり、反復処理を行うこともできます。 –

1

これはあなたの後ろのことです(私は思う!)ICollection<T>を実装し、内部の_storage辞書にその呼び出しを委任することもできます。あなたがやりたいことは何

public class HybridLookup<TKey, TValue> : IEnumerable<TValue> 
{ 
    private readonly IDictionary<TKey, TValue> _storage; 

    public HybridLookup() 
    { 
     _storage = new Dictionary<TKey, TValue>(); 
    } 

    public TValue this[TKey key] 
    { 
     get { return _storage[key]; } 
    } 

    public Boolean Contains(TKey key) 
    { 
     return _storage.ContainsKey(key); 
    } 
    public IEnumerator<TValue> GetEnumerator() 
    { 
     return _storage.Values.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

} 
1

私はあなたがコードのあなたの最後のスニペットで提案された解決策は、マイナーな修正を必要とAddDepartment方法を除き、正常に動作と思う:

public void AddDepartment(Department department) 
{ 
    DepartmentsByUniqueName.Add(department.UniqueName, department); 
} 

Containsが既にDictionary<TKey, TValue>.ValueCollection上に存在する、とあなたはLINQを使用することができますインデクサの代わりにElementAtメソッドを使用します(ただし、効率的ではありません)。

Dictionary<TKey,TValue>.Valuesコレクションを変更するコンシューマについて心配する必要はありません。このような試みは、NotSupportedException:「辞書から派生した値コレクションの変更を許可していません。ここで

はクラスから関連するコードの抜粋です:あなたが反復する必要がある場合

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback 
{ 
    public Dictionary<TKey, TValue>.ValueCollection Values { get; } 

    public sealed class ValueCollection : ICollection<TValue>, IEnumerable<TValue>, ICollection, IEnumerable 
    { 
     void ICollection<TValue>.Add(TValue item) 
     { 
      ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ValueCollectionSet); 
     } 
    } 
} 
関連する問題