2013-04-18 3 views
3

Я ищу, чтобы узнать, есть ли способ легко выставить мои контейнеры из классов C#, не раскрывая ненужных деталей реализации. Я изучаю C#, но у меня есть опыт работы на других языках OO, поэтому я знаю, что там, где это возможно, лучше показывать базовые классы/интерфейсы, а не конкретные типы. То, что я делаю, - это хранение коллекций ключа (коллекции объекта) в моем классе. Я нахожу, что я могу легко разоблачить это как IDictionary (ключ, коллекция объекта), но мне хотелось бы IDictionary (key, IEnumerable (object)). Надеюсь, простой пример сделает его более ясным.Удобный способ опубликовать значение словаря в качестве базовых типов

class AddressBook 
{ 
    public IDictionary<string, List<string>> Name2Addresses // ok, but I want IDictionary<string, IEnumerable<string>> 
    { 
     get { return name2Addresses; } 
    } 

    public IEnumerable<string> GetAddresses(string name)// ok, but I really just want the convenience of exposing container as a "one-liner" 
    { 
     return name2Addresses[name]; 
    } 

    public AddressBook() 
    { 
     name2Addresses = new Dictionary<string,List<string>>(); 
     List<string> addresses = new List<string>(); // I'd prefer IList<string> addresses = ... 
     name2Addresses.Add("Anne Best", addresses); 
     addresses.Add("Home address"); 
     addresses.Add("Work address"); 
    } 
    Dictionary<string, List<string>> name2Addresses; 
} 

Я знаю, что разоблачение контейнера, полученные в качестве контейнера базы является сложной проблемой в OO, потому что, например, можно добавить другую, полученные с помощью контейнера базового класса. Но, надеюсь, потому что я хочу показать контейнер как доступный только для чтения через IEnumerable, может быть простой способ сделать это. Это код, который я хочу.

public IDictionary<string, IEnumerable<string>> Name2Addresses 
    { 
     get { return name2Addresses; } 
    } 

Я попытался добавить слепок как компилятор жаловался, не имея один

public IDictionary<string, IEnumerable<string>> Name2Addresses 
    { 
     get { return (IDictionary<string, IEnumerable<string>>) name2Addresses; } 
    } 

, но потом я получил исключение:

Дополнительная информация: Не удается привести объект типа 'System.Collections.Generic.Dictionary 2[System.String,System.Collections.Generic.IList 1 [System.String]]' для ввода 'System.Collections.Generic.IDictionary 2[System.String,System.Collections.Generic.IEnumerable 1 [System.String]] '.

Любые идеи? Я надеялся, что может быть легкий/элегантный способ сделать это, так как в целом было радостью перейти от языка OO более низкого уровня к C#, где гораздо быстрее и проще использовать то, что я хочу на практике. Это не шоу-стоппер каким-либо образом, поскольку я могу просто раскрыть List, а не IEnum и доверять себе, чтобы не злоупотреблять списком, но я хотел бы сделать все правильно, особенно, поскольку, надеюсь, у меня будут другие люди, которые однажды используют мой код ,

PS Я помню, где-то читал, что C# недавно улучшил поддержку такого рода вещей. Я на VS2010, если это имеет значение.

Редактировать

Я сделал некоторые поиски, прежде чем спрашивать, но после еще некоторых поисков, я нашел, что это было предложено ранее, в несколько ином контексте, Why can't Dictionary<T1, List<T2>> be cast to Dictionary<T1, IEnumerable<T2>>?. Так что мой вопрос, возможно, сводится к есть ли способ, имеющие только для чтения IDictionary (см также Is there a read-only generic dictionary available in .NET?)

других Редактировать

Как предложил Алекс G/code4life, это будет работать: без прокатки собственных чтений - Только класс словаря я могу создать новый словарь. Например.

public IDictionary<string, IEnumerable<string>> Name2AddressesNewDictionary 
    { 
     get 
     { 
      return name2Addresses.ToDictionary(na=>na.Key, na=>na.Value.AsEnumerable()); 
     } 
    } 

Это влечет снижение производительности каждый раз, когда доступ + пробел штраф создание новых пар ключ-значение (и сам Dict). Кроме того, дополнительное время для разработчиков. Не кажется лучше, чем разоблачение Перечня, в общем, если я использую свойство (особенно, поскольку работа продолжается под обложками). Но было бы хорошо как метод/в других обстоятельствах.

Третий Edit

Как предложил Jens Kloster, изменить определение контейнера использовать IEnumerable в качестве значения

Dictionary<string, IEnumerable<string>> name2Addresses; 

и затем отливали, когда вам нужно в реализации.

 ((IList<string>)name2Addresses["Anne Best"]).Add("Alternative address"); 
+0

предложил Ваше решение является слишком трудоемким. Я бы предложил либо определить «Словарь <строка, IEnumerable >», либо использовать 'name2Addresses.ToDictionary (k - => key, v => v.Value.AsEnumerable()) вместо этого - это как исправления 1-лайнера к вашей проблеме. – code4life

+0

Да, я в целом согласен с тем, что вы пишете, и меня поправляет мой вопрос. У меня просто проблемы с редактированием достаточно быстро, чтобы идти в ногу со всеми полезными предложениями! (первый раз я задал вопрос о SO, v доволен ответом) – TooTone

ответ

3

попробуйте изменить поле, чтобы использовать IEnumerable вместо List.

Dictionary<string, IEnumerable<string>> name2Addresses; 

, то вы не должны иметь никаких проблем, имеющий свойство как это:

public IDictionary<string, IEnumerable<string>> Name2Addresses 
{ 
    get { return name2Addresses; } 
} 

и вы все еще можете это сделать:

public AddressBook() 
    { 
     name2Addresses = new Dictionary<string,IEnumerable<string>>(); //changed ere 
     List<string> addresses = new List<string>(); 
     name2Addresses.Add("Anne Best", addresses); 
     addresses.Add("Home address"); 
     addresses.Add("Work address"); 
    } 

также - иметь взгляд на this пост

+0

Проблема с этим подходом заключается в том, что вам нужно будет перечислить обратно список «Список », если вы хотите изменить список, и классы-потребители могут сделать то же самое. – AlexFoxGill

+0

@ AlexG есть. но зачем вам это делать. какой смысл использовать «IEnumerable», если вы просто собираетесь использовать его для конкретного типа? :) –

+0

Я думаю, что идея в том, что в какой-то момент в классе хоста вы можете захотеть добавить или удалить адрес, но когда вы выставляете словарь снаружи, вы не хотите, чтобы потребители могли делать то же самое. Честно говоря, лучший способ - создать настраиваемую структуру данных ...! – AlexFoxGill

1

Вы можете посмотреть в) метод .AsEnumerable (расширение, которое доступно через System.Linq:

http://msdn.microsoft.com/en-us/library/bb335435.aspx

+0

спасибо, не уверен, что это помогает, хотя и не уверен, как я буду использовать его для выполнения того, что хочу.Ваш ответ вдохновил меня немного по-другому найти ответ на мой вопрос, поэтому редактирование на мой оригинальный пост. – TooTone

+0

Я получаю это сейчас ... – TooTone

1

Действительно, лучший выход будет доступен только для чтения ковариантны словарь интерфейс, но нет никого. Однако вы можете легко написать реалистичную версию IDictionary, которая бы обернула основной словарь и литые элементы до IEnumerable<T>.

+0

спасибо, удивлен, что microsoft или a.n.other не сделали этого, поскольку это кажется общей проблемой, и я понимаю, что поддержка ковариации/контравариантности в последнее время улучшилась. – TooTone

+0

Согласен, это проблема ковариации. Именно поэтому определение словаря для использования 'IEnumerable ', вероятно, является более разумной задачей. – code4life

+0

, отвечая на мой вопрос о том, почему это еще не сделано, благодаря ссылке, которую разместил Йенс: http://stackoverflow.com/a/7507943/834521 – TooTone

4

Вы не можете бросить, из-за covariance/contravariance, но вы можете воссоздать словарь в формате, который вы хотите с помощью LINQ:

using System.Linq; ...

public IDictionary<string, IEnumerable<string>> Name2Addresses 
{ 
    get 
    { 
     return name2Addresses.ToDictionary<string, IEnumerable<string>>(
      kvp => kvp.Key, 
      kvp => kvp.Value.AsEnumerable()); 
    } 
} 

Примечание: это создает новый объект словаря !

В более общей заметке, связанной с дизайном: в любое время, когда вы обнаруживаете, что вы объединяете структуры данных таким образом, обычно важно определить свою собственную структуру данных, чтобы обеспечить ясность и выставить необходимые методы, которые вам понадобятся для прохождения структуры и выполнения общих операций.

+0

спасибо, я редактировал свой вопрос одновременно - - это хороший способ подумать об этом. – TooTone

+0

Он создает новый словарь, но вы должны заметить, что экземпляры элементов (обычно ...) не воссозданы. – code4life

+0

Ваше примечание важно здесь. Многие решения (включая отправленные здесь) воссоздают словарь, который ужасно неэффективен. – BJury

1

Я думаю, что вам просто нужно снова LINQ-ify.

public IDictionary<string, IEnumerable<string>> Name2Addresses  
{ 
    get 
    { 
    return name2Addresses.ToDictionary(d => d.Key, d => d.Value.AsEnumerable()); 
    } 
} 

С другой стороны, пересмотреть словарь использовать IEnumerable - это может быть лучшим способом сделать это .:

Dictionary<string, IEnumerable<string>> name2Addresses; 
Смежные вопросы