IDictionary, то есть IEnumerable <ValueType>вместо KeyValue? - PullRequest
0 голосов
/ 28 января 2012

Я хочу иметь возможность искать значения по ключу в тех же случаях, а в других случаях выполнять итерацию по коллекции.

Ранее я делал это:

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 ...

Теперь у меня есть несколько мест, которые мне также нужно перебрать в какой-то другой коллекции, например, списки флажков с соответствующими строками для UniqueName, поэтому для большей эффективности я изменил объявление Departments на IDictionary, но это делает другие варианты использования более громоздким, всегда требуя от меня углубиться в стоимость или ценности. Таким образом, теперь свойство 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 ...

Мне также не важно преобразовывать списки в IDictionary или добавлять KeyValue всякий раз, когда я инициализирую эти списки.

В идеале у меня должна быть коллекция, которая ведет себя как ICollection, но также имеет оператор индекса и содержит функцию, которая обеспечивает внутренний доступ к словарю.

ИЛИ у меня было бы два свойства, которые так синхронизируются:

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)
}

Проблема здесь в том, что кто-то может получить коллекцию Values ​​через свойство Departments и добавить / удалить элементы к нему, не осознавая, что они действительно должны быть добавлены в словарь вместо этого (реализация set не решает эту проблему, потому что они получают собрание через get, а затем может добавлять элементы к нему.)

Попытка не изобретать слишком много кода, но наследование от словаря автоматически делает его наследником IEnumerable. Даже если я сделаю явную реализацию интерфейса, могут возникнуть проблемы, если пользователь приведёт его к этому базовому интерфейсу.

Так что, по сути, я хочу класс, который реализует IEnumerable, но также имеет операторы Contains и [], которые для большей эффективности используют словарь.

ИЛИ сможете создать дополнительное свойство словаря DepartmentByUniqueName, которое будет синхронизировано с коллекцией Departments.

Ответы [ 3 ]

3 голосов
/ 28 января 2012

Что вы можете сделать, это создать подкласс KeyedCollection и определить его следующим образом:

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

И использовать его в своем классе компании в качестве свойства Departments:

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
1 голос
/ 28 января 2012

Я думаю, что решение, которое вы предложили в своем последнем фрагменте кода, работает нормально, за исключением метода 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);
        }
    }
}
1 голос
/ 28 января 2012

Это делает то, что вы после (я думаю!) Вы могли бы также реализовать 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();
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...