2016-07-28 3 views
1

Я пытаюсь гибко использовать интерфейс IDictionary в одном из моих методов, но из-за ограничений в отношении преобразования (см. C# type conversion: Explicit cast exists but throws a conversion error?) его использование очень ограничено для меня. Я хотел бы знать, есть ли способ обхода проблемы.Гибкое использование IDictionary (с интерфейсами)

Это моя конкретная проблема: У меня есть метод, который берет отображение, которое отображает каждую клавишу в IEnumerable из других ключей. Он также принимает один ключ в качестве входных данных. Что она делает это, чтобы найти закрытие набора/корпус данного ключа относительно отображения:

public static ISet<T> GetClosureSet(T element, IDictionary<T, IEnumerable<T>> elementToCollectionMap) 
{ 
    ISet<T> closure = new HashSet<T>(); 
    closure.Add(element); 
    closure.UnionWith(elementToCollectionMap[element]); 

    int count = 0; 
    while (count != closure.Count) 
    { 
     count = closure.Count; 
     foreach (T elem in new HashSet<T>(closure)) 
      closure.UnionWith(elementToCollectionMap[elem]); 
    } 

    return closure; 
} 

Пример такого отображения типа IDictionary<double, IEnumerable<double>>:

1 -> [2, 3, 4] 
2 -> [3, 7] 
3 -> [3] 
4 -> [] // empty enumerable, i.e. array of length 0 
5 -> [6] 
6 -> [6] 
7 -> [] 

Если я положил ключ 1 и это сопоставление в моем методе, я получу [1, 2, 3, 4, 7]: сначала 1 и его изображение [2, 3, 4] собраны в комплект укупорки. Затем добавляются изображения 1, 2, 3, 4, поэтому мы также получаем 7 (в качестве элемента изображения 2). На следующем этапе все изображения 1, 2, , 4, 7 добавлены, но они уже находятся там. Таким образом, метод заканчивается и возвращается.

Как вы видите, это очень абстрактный метод, который не заботится о том, каковы ценности на самом деле. Для того, чтобы позвонить, UnionWith, нужны только значения IEnumerable<T>.

Но теперь я хочу иметь возможность использовать метод всякий раз, когда у меня есть сопоставление от ключей к каким-то коллекциям ключей!

У меня есть некоторые места в моем коде, где я определяю

IDictionary<MyType, HashSet<MyType>> foo = new Dictionary<MyType, HashSet<MyType>>(); 

и

IDictionary<MyType, List<MyType>> bar = new Dictionary<MyType, List<MyType>>(); 

и нужно их действительно IDictionary<MyType, HashSet<MyType>> и IDictionary<MyType, List<MyType>>, потому что мне нужна функциональность HashSet и List кроме того, что предоставлено IEnumerable. Только позже я хочу получить закрытие. Но как сейчас, я не могу дать foo и bar в качестве входных данных для моего метода. Мне нужно создать из них новые словари, чтобы они соответствовали типу.

Любые идеи о том, как решить проблему (я не считаю «создание нового словаря для соответствия типу« решение »)?

ответ

2

Лучший способ решить эту проблему - внести незначительные изменения в ваш общедоступный API.Фундаментально GetClosureSet не требуется отображение всех ключей к последовательности значений для этого ключа, он просто нуждается в операции, чтобы получить все значения для данного ключа:

public static ISet<T> GetClosureSet(
    T element, 
    Func<T, IEnumerable<T>> childSelector) 

Абонент может реализовать этот метод в любое количество способов, один из которых выполняет поиск в словаре, который у них есть. Это фактически делает ваш метод еще более вообще, поскольку он позволяет выполнять эту операцию на разных типах графиков, которые хранят их узлы совершенно по-другому, например объекты «Узла», у которых уже есть ссылка на коллекцию детей, а чем графики, хранящиеся в словаре.

+0

Can 'Funct ' have "no value" для некоторого ввода типа 'T', как' Словарь'? – Kjara

+1

@Kjara Если объект не имеет сопоставлений, то этот селектор должен возвращать пустую последовательность. Учитывая, что ваш словарь уже хранит пустую последовательность в качестве значения для этого ключа, вызывающему нужно написать «element => foo [element]». Конечно, если другой вызывающий абонент хранит свой граф в другой усадьбе целиком, они могут назвать это по-другому. – Servy

+0

И может 'Func ' be null? (Просто проверьте, нужно ли мне что-то менять в моем коде относительно недопустимого ввода.) – Kjara

2

Вы можете добавить параметр для типа коллекции:

public static ISet<T> GetClosureSet<T, C>(T element, IDictionary<T, C> elementToCollectionMap) where C : IEnumerable<T> { ... } 

примечание компилятор не сможет сделать вывод C для вас.

Смежные вопросы