Tytuł jest wystarczająco prosty, dlaczego nie mogę:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
c#
.net
dictionary
Custodio
źródło
źródło
Odpowiedzi:
Komentarz do pierwotnego pytania podsumowuje to całkiem dobrze:
A dlaczego? Cóż, prawdopodobnie dlatego, że nie można uzasadnić zachowania scalania słowników w sposób zgodny z wytycznymi Framework.
AddRange
nie istnieje, ponieważ zakres nie ma żadnego znaczenia dla kontenera asocjacyjnego, ponieważ zakres danych pozwala na zduplikowane wpisy. Np. Jeśli masz takąIEnumerable<KeyValuePair<K,T>>
kolekcję, nie chroni przed zduplikowanymi wpisami.Zachowanie polegające na dodaniu kolekcji par klucz-wartość lub nawet scaleniu dwóch słowników jest proste. Jednak nie jest to sposób postępowania z wieloma zduplikowanymi wpisami.
Jakie powinno być zachowanie metody w przypadku duplikatu?
Są co najmniej trzy rozwiązania, które przychodzą mi do głowy:
Jaki powinien być stan oryginalnego słownika po zgłoszeniu wyjątku?
Add
jest prawie zawsze implementowana jako operacja atomowa: kończy się powodzeniem i aktualizuje stan kolekcji lub kończy się niepowodzeniem, a stan kolekcji pozostaje niezmieniony. PonieważAddRange
może się nie powieść z powodu zduplikowanych błędów, sposobem zachowania spójności z jego zachowaniemAdd
byłoby również uczynienie go niepodzielnym przez rzucenie wyjątku na dowolny duplikat i pozostawienie stanu oryginalnego słownika bez zmian.Jako konsument interfejsu API byłoby żmudne iteracyjne usuwanie zduplikowanych elementów, co oznacza, że
AddRange
powinien zgłosić pojedynczy wyjątek zawierający wszystkie zduplikowane wartości.Wybór sprowadza się wówczas do:
Istnieją argumenty za wsparciem obu przypadków użycia. Aby to zrobić, czy dodajesz
IgnoreDuplicates
flagę do podpisu?IgnoreDuplicates
Flag (gdy wartość true) będzie również zapewniać znaczną prędkość w górę, jako podstawowej implementacji ominie kodzie duplikatu kontroli.Więc teraz masz flagę, która pozwala na
AddRange
obsługę obu przypadków, ale ma nieudokumentowany efekt uboczny (nad którym projektanci Framework naprawdę ciężko pracowali).Podsumowanie
Ponieważ nie ma jasnego, spójnego i oczekiwanego zachowania, jeśli chodzi o postępowanie z duplikatami, łatwiej jest nie zajmować się nimi wszystkimi razem i nie zapewnić metody na początek.
Jeśli ciągle będziesz musiał scalać słowniki, możesz oczywiście napisać własną metodę rozszerzenia, aby scalić słowniki, które będą zachowywać się w sposób odpowiedni dla Twojej aplikacji (-ów).
źródło
AddMultiple
różni się odAddRange
tego, niezależnie od tego, czy jego implementacja będzie wątpliwa: czy rzucasz wyjątek z tablicą wszystkich zduplikowanych kluczy? A może rzucasz wyjątek na pierwszy napotkany duplikat klucza? Jaki powinien być stan słownika, jeśli zostanie zgłoszony wyjątek? Nieskazitelny, czy wszystkie klucze, którym się udało?Add
- albo zawiń każdąAdd
w atry...catch
i złap w ten sposób duplikaty; lub użyj indeksatora i nadpisz pierwszą wartość późniejszą; lub zapobiegawczo sprawdź użycieContainsKey
przed próbąAdd
, zachowując w ten sposób oryginalną wartość. Gdyby struktura miała metodęAddRange
lubAddMultiple
, jedynym prostym sposobem przekazania informacji o tym, co się stało, byłby wyjątek, a obsługa i odzyskiwanie danych byłyby nie mniej skomplikowane.Mam rozwiązanie:
...
Baw się dobrze.
źródło
ToList()
, słownik to plikIEnumerable<KeyValuePair<TKey,TValue>
. Ponadto druga i trzecia metoda metody zostanie wyświetlona, jeśli dodasz istniejącą wartość klucza. Nie jest to dobry pomysł, szukałeśTryAdd
? Ostatecznie drugiego można zastąpićWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
to nie jest dobre rozwiązanie, więc zmieniłem kod. Możesz użyć,try { mainDic.AddRange(addDic); } catch { do something }
jeśli nie jesteś pewien trzeciej metody. Druga metoda działa idealnie.W przypadku, gdy ktoś natknie się na to pytanie tak jak ja - możliwe jest osiągnięcie "AddRange" za pomocą metod rozszerzających IEnumerable:
Główną sztuczką podczas łączenia słowników jest radzenie sobie ze zduplikowanymi kluczami. W powyższym kodzie jest to część
.Select(grp => grp.First())
. W tym przypadku po prostu pobiera pierwszy element z grupy duplikatów, ale w razie potrzeby można tam zaimplementować bardziej wyrafinowaną logikę.źródło
dict1
nie używa domyślnej funkcji porównującej równość?var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Domyślam się, że użytkownik nie ma odpowiednich informacji o tym, co się stało. Ponieważ w słownikach nie można mieć powtarzających się kluczy, jak poradziłbyś sobie z łączeniem dwóch słowników, w których krzyżują się niektóre klucze? Oczywiście można powiedzieć: „Nie obchodzi mnie to”, ale to łamie konwencję zwracania fałszu / wyrzucania wyjątku dla powtarzających się kluczy.
źródło
Add
, poza tym, że może się to zdarzyć więcej niż raz. Rzuciłby to samoArgumentException
,Add
co na pewno?NamedElementException
??), albo wyrzucony zamiast lub jako wewnętrzny wyjątek ArgumentException, który określa nazwany element, który powoduje konflikt ... kilka różnych opcji, powiedziałbymMógłbyś to zrobić
lub użyj listy do dodawania i / lub używając powyższego wzorca.
źródło
MethodThatReturnAnotherDic
. Pochodzi z PO. Proszę ponownie przejrzeć pytanie i moją odpowiedź.Jeśli masz do czynienia z nowym słownikiem (i nie masz istniejących wierszy do stracenia), zawsze możesz użyć ToDictionary () z innej listy obiektów.
Więc w twoim przypadku zrobiłbyś coś takiego:
źródło
Dictionary<string, string> dic = SomeList.ToDictionary...
Jeśli wiesz, że nie będziesz mieć zduplikowanych kluczy, możesz:
Zgłosi wyjątek, jeśli istnieje zduplikowana para klucz / wartość.
Nie wiem, dlaczego nie ma tego w ramach; Powinien być. Nie ma niepewności; po prostu wyrzuć wyjątek. W przypadku tego kodu zgłasza wyjątek.
źródło
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
?Zapraszam do korzystania z takiej metody rozszerzenia:
źródło
Oto alternatywne rozwiązanie wykorzystujące C # 7 ValueTuples (literały krotki)
Używane jak
źródło
Jak wspominali inni, powodem, dla którego
Dictionary<TKey,TVal>.AddRange
nie został zaimplementowany, jest to, że istnieje wiele sposobów obsługi przypadków, w których masz duplikaty. Jest to również przypadekCollection
lub interfejsy, takie jakIDictionary<TKey,TVal>
,ICollection<T>
itpTylko
List<T>
implementuje to, a zauważysz, żeIList<T>
interfejs nie działa z tych samych powodów: oczekiwane zachowanie podczas dodawania zakresu wartości do kolekcji może się znacznie różnić w zależności od kontekstu.Kontekst twojego pytania sugeruje, że nie martwisz się o duplikaty, w takim przypadku masz prostą alternatywę oneliner, używając Linq:
źródło