Vraag Hoe maak je een nieuw woordenboek <,> van een IReadOnlyDictionary <,>?


.NET 4.5 introduceert het handige IReadOnlyDictionary<TKey, TValue> interface. EEN Dictionary<TKey, TValue>  is een  IReadOnlyDictionary<TKey, TValue>, dus ik kan de eerste passeeren waar deze laatste vereist is.

Ik ben echter niet zeker van de tegenovergestelde manier: Hoe maak ik een nieuwe aan Dictionary<,> gebaseerd op een bestaand IReadOnlyDictionary<,>?

  • Dictionary<,> heeft verschillende constructeurs die een IDictionary<,> - maar geen die een IReadOnlyDictionary<,>.
  • IReadOnlyDictionary<,> heeft een uitbreidingsmethode ToDictionary<,>(). Dat is echter geërfd van IEnumerable<>. Dus om het te gebruiken, moet ik twee uiterst overbodige afgevaardigden als deze doorgeven: readOnlyDict.ToDictionary(pair => pair.Key, pair => pair.Value). Lelijk!
  • Natuurlijk kan ik altijd een nieuw maken Dictionary<,>, itereer de originele paren en kopieer ze over.

Het lijkt mij dat er een triviale manier moet zijn om een ​​nieuw te maken Dictionary<,> gebaseerd op een bestaand IReadOnlyDictionary<,>. Mis ik iets?

Bewerk: Enige verduidelijking. Ik ben niet op zoek naar een of andere magische manier om een ​​te behandelen IReadOnlyDictionary<,> als een Dictionary<,>. Ik wil een nieuw maken Dictionary<,> die de beginwaarden kopieert van een IReadOnlyDictionary<,>. Deze vraag is een langdradige manier om te vragen:

Dictionary<,> heeft een convenience-constructor om de beginwaarden van een te kopiëren IDictionary<,>. Hoe komt het dat er geen een neemt IReadOnlyDictionary<,> en wat is de meest idiomatische manier om hetzelfde resultaat te bereiken?


17
2018-02-23 08:53


oorsprong


antwoorden:


Dictionary<TKey, TValue> kan implementeren IReadOnlyDictionary<TKey, TValue>, omdat elke code die dit laatste doet veelbelovend is en niet het woordenboek, dat een subset is van de functionaliteit die het eerste biedt, wijzigt.

Maar overweeg de andere richting. Je begint met een IReadOnlyDictionary<TKey, TValue>en je probeert het een gewoonte te maken Dictionary<TKey, TValue>. Nu, je hebt iets meegenomen dat eerder beloofd was om niet te worden aangepast en veranderde het in iets dat kan worden aangepast.

Het is duidelijk dat dat gewoon niet zal lukken.

Bovendien kun je er niet zomaar van uitgaan dat de alleen-lezen implementatie een uitzondering of iets anders zou opleveren bij wijziging (bijv. Runtime-beveiliging, hoewel niet de compile-time-veiligheid), omdat je, zoals je weet, altijd alleen-lezen kunt krijgen interface van een veranderlijke implementatie.

Dus om veilig te zijn, heb je twee opties:

  1. Kopieer de volledige inhoud van het woordenboek naar een nieuw object.
  2. Wikkel het alleen-lezen object in een IDictionary<TKey, TValue> implementatie dat doet gooi een uitzondering als je het probeert te wijzigen.

Merk op dat deze laatste je niet echt geeft waar je specifiek om vraagt. Het is dichtbij, maar het is geen echte instantie van Dictionary<TKey, TValue>.

Qua kopiëren heb je veel keuzes. Persoonlijk denk ik ToDictionary() is zelf prima. Maar als je de breedsprakigheid niet echt leuk vindt, kun je deze omzetten in een uitbreidingsmethode:

public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(
    this IReadOnlyDictionary<TKey, TValue> dict)
{
    return dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
}


Addendum:
Het komt mij voor dat ik dit waarschijnlijk moet verduidelijken: het gaat er vooral van uit dat je een probeert te converteren arbitrair instantie van IReadOnlyDictionary<TKey, TValue>. Vanzelfsprekend, als u weet of tenminste vermoedt dat uw IReadOnlyDictionary<TKey, TValue> object is in feite een instantie van Dictionary<TKey, TValue>, kunt u (respectievelijk) rechtstreeks casten of proberen te casten Dictionary<TKey, TValue>.


11
2018-02-23 09:17



Het lijkt erop dat IReadOnlyDictionary<> is later toegevoegd en bestaande klassen (zoals Dictionary<>) werden niet achteraf ingebouwd om het te ondersteunen nieuwe methoden, om redenen van achterwaartse compatibiliteit. [nodig citaat].

Voor wat het waard is, Microsoft's referentie-implementatie van DictionaryDe constructeur is in wezen

Dictionary(IDictionary dictionary)
: this(dictionary.Count) {
    foreach (KeyValuePair<TKey,TValue> pair in dictionary) {
            Add(pair.Key, pair.Value);
    }
}

En Enumerable is in wezen

Dictionary<> ToDictionary<>(this IEnumerable<> source, Func<> keySelector, Func<> elementSelector) 
{
    Dictionary<> d = new Dictionary<>();
    foreach (TSource element in source) 
    d.Add(keySelector(element), elementSelector(element));
    return d;
}

Het verschil lijkt dus vooral te bestaan ​​uit de oproepen aan de selectorafgevaardigden.

Dus een uitbreidingsmethode die de code van de constructor kopieert maar een IReadOnlyDictionary<>, min of meer zoals @Steve deed, lijkt iets eenvoudiger en iets efficiënter ...


1
2017-08-10 23:26



Het vaak geciteerde antwoord voor "waarom heeft C # deze eigenschap niet" is dat het tijd kost om te ontwerpen, te implementeren, te documenteren, te testen, enz., Vooral wanneer rekening moet worden gehouden met bron- en binaire compatibiliteit met bestaande code.

Intuïtief, elk IDictionary<,> zou ook een zijn IReadOnlyDictionary<,> (de laatste is een subset van de interface van de eerste), en misschien als IReadOnlyDictionary<,> Bestond vanaf het begin dat het op die manier zou worden opgezet (met de relevante constructeur die het zou nemen IReadOnlyDictionary<,> als zijn argument, en automatisch een accepteren IDictionary<,> vanwege erfenis), maar zoals de zaken er nu voorstaan IDictionary<,> en IReadOnlyDictionary<,> zijn niet-gerelateerde interfaces die toevallig dezelfde functies bevatten. Ik kan alleen maar aannemen dat de reden dat ze niets met elkaar te maken hebben, is om te voorkomen dat bestaande gebruikerscodes worden verbroken die een expliciete interface-implementatie van doen IDictionary<,>.

Als zodanig neemt een nieuwe constructeur een IReadOnlyDictionary<TKey, TValue> zou een nieuwe niet-gerelateerde functie zijn die toevallig dezelfde code bevat als de bestaande constructor die een IDictionary<TKey, TValue>of een of andere constructor zou een wrapper moeten gebruiken.

Ik kan alleen maar aannemen dat dit niet belangrijker werd geacht dan andere mogelijke functies in de pijplijn ...

... en het feit dat toen ik hetzelfde probleem tegenkwam, het vrij moeilijk was om de bestaande vraag te vinden (en zelfs dan was het er 1,5 jaar geweest zonder een antwoord op de vraag die werd gesteld), suggereert dat ze gelijk hadden prioriteit geven aan andere, belangrijker functies.

In mijn eigen geval had ik een klas die is afgeleid van Dictionary<MyEnum,double> (om een ​​woordenboek te hebben dat ook een interface implementeert door toegang te hebben tot specifieke sleutels), en wilde een constructor schrijven zoals:

public MyDictionary(IReadOnlyDictionary<MyEnum, double> source) : base(source)
{ }

In plaats daarvan schreef ik, nadat we hadden geprobeerd om in iemand anders een soortgelijk probleem te zien (en deze vraag te vinden), iets dat lijkt op het volgende:

public MyDictionary(IReadOnlyDictionary<MyEnum, double> source)
{
  foreach (var pair in source)
    base.Add(pair.Key, pair.Value);
}

In gevallen waarin we rechtstreeks een Dictionary<,>, er zijn minstens twee perfect leesbare opties - een lus vergelijkbaar met het bovenstaande, of de ToDictionary uitbreidingsfunctie die de oorspronkelijke vraag alleen verwierp omdat hij "lelijk" was (ik verwierp het omdat het zou betekenen dat het woordenboek twee keer werd gekopieerd - eenmaal in de ToDictionary call en eenmaal in de constructor van de basisklasse).

Ik zou de "ontbrekende" constructor verwachten Dictionary<,> om even triviaal te zijn in de implementatie, maar ze moeten ook omgaan met alle andere bijbehorende palaver die bij elke nieuwe functie hoort. Dictionary<,> en IDictionary<,> worden ZEER veel gebruikt op allerlei interessante manieren, waardoor het potentieel om dingen te breken zelfs met de eenvoudigste en meest onschadelijke veranderingen nog groter wordt.

Bewerk:Denken verder ... als de "ontbrekende" constructor zou worden geïmplementeerd, zou het dubbelzinnig maken welke constructeur zou moeten worden opgeroepen voor een klasse die implementeert beide interfaces IDictionary<,> en IReadOnlyDictionary<,>. Het beste voorbeeld is de Dictionary-klasse zelf.


0
2017-11-08 11:44