Erstellen eines konstanten Wörterbuchs in C #

128

Was ist der effizienteste Weg, um eine konstante (sich zur Laufzeit nie ändernde) Zuordnung von strings zu ints zu erstellen ?

Ich habe versucht, ein const Dictionary zu verwenden , aber das hat nicht geklappt.

Ich könnte einen unveränderlichen Wrapper mit entsprechender Semantik implementieren , aber das scheint immer noch nicht ganz richtig zu sein.


Für diejenigen, die gefragt haben, implementiere ich IDataErrorInfo in einer generierten Klasse und suche nach einer Möglichkeit, die Suche nach columnName in mein Array von Deskriptoren aufzunehmen.

Ich war mir nicht bewusst (Tippfehler beim Testen! D'oh!), Dass der Schalter Zeichenfolgen akzeptiert, also werde ich ihn verwenden. Vielen Dank!

David Schmitt
quelle
1
Hier gibt es eine Lösung: stackoverflow.com/questions/10066708/…

Antworten:

178

Das Erstellen eines wirklich zur Kompilierungszeit generierten konstanten Wörterbuchs in C # ist keine einfache Aufgabe. Eigentlich erreicht keine der Antworten hier wirklich das.

Es gibt jedoch eine Lösung, die Ihren Anforderungen entspricht, wenn auch nicht unbedingt eine gute. Denken Sie daran, dass gemäß der C # -Spezifikation Switch-Case-Tabellen zu konstanten Hash-Sprungtabellen kompiliert werden. Das heißt, es handelt sich um konstante Wörterbücher, nicht um eine Reihe von if-else-Anweisungen. Betrachten Sie also eine Switch-Case-Anweisung wie folgt:

switch (myString)
{
   case "cat": return 0;
   case "dog": return 1;
   case "elephant": return 3;
}

Genau das wollen Sie. Und ja, ich weiß, es ist hässlich.

Tamas Czinege
quelle
9
Es generiert sowieso Code, hat gute Leistungseigenschaften, kann zur Kompilierungszeit berechnet werden und hat auch andere nette Eigenschaften für meinen Anwendungsfall. Ich werde es so machen!
David Schmitt
4
Achten Sie nur darauf, dass die Rückgabe von Nicht-Wert-Typen, auch wenn dies der Fall ist, die Unveränderlichkeit Ihrer Klassen beeinträchtigt.
Tom Anderson
35
Switch-Case ist fantastisch, bis Sie durch die Werte "foreach" müssen.
Alex
6
Es fehlen viele nette Funktionen, die Wörterbücher haben.
Zinan Xing
1
@ TomAnderson: Das Konstrukt verhält sich unveränderlich, wenn es sich bei den zurückgegebenen Elementen um Werttypen handelt (veränderbar oder nicht veränderbar) oder wenn es sich um unveränderliche Klassenobjekte handelt.
Supercat
37

Im aktuellen Rahmen gibt es nur wenige unveränderliche Sammlungen. Ich kann mir eine relativ schmerzfreie Option in .NET 3.5 vorstellen:

Verwendung Enumerable.ToLookup()- Die Lookup<,>Klasse ist unveränderlich (aber auf der rechten Seite mehrwertig). Sie können dies Dictionary<,>ganz einfach tun :

    Dictionary<string, int> ids = new Dictionary<string, int> {
      {"abc",1}, {"def",2}, {"ghi",3}
    };
    ILookup<string, int> lookup = ids.ToLookup(x => x.Key, x => x.Value);
    int i = lookup["def"].Single();
Marc Gravell
quelle
14
enum Constants
{
    Abc = 1,
    Def = 2,
    Ghi = 3
}

...

int i = (int)Enum.Parse(typeof(Constants), "Def");
Richard Poole
quelle
2
Interessante Idee! Ich frage mich, wie gut der Parse () -Aufruf funktioniert. Ich fürchte, nur ein Profiler kann darauf antworten.
David Schmitt
10

Dies ist die nächste Sache, die Sie zu einem "CONST Dictionary" bekommen können:

public static int GetValueByName(string name)
{
    switch (name)
    {
        case "bob": return 1;
        case "billy": return 2;
        default: return -1;
    }
}

Der Compiler ist intelligent genug, um den Code so sauber wie möglich zu erstellen.

Timothy Khouri
quelle
8

Bei Verwendung von 4.5+ Framework würde ich ReadOnlyDictionary (auch ReadOnly Collection für Listen) verwenden, um schreibgeschützte Zuordnungen / Konstanten zu erstellen. Es wird folgendermaßen implementiert.

static class SomeClass
{
    static readonly ReadOnlyDictionary<string,int> SOME_MAPPING 
        = new ReadOnlyDictionary<string,int>(
            new Dictionary<string,int>()
            {
                { "One", 1 },
                { "Two", 2 }
            }
        )
}        
Kram
quelle
private static readonly Dictionary<string, string> _yourDictionaryName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { { "One",1 }, { "Two",2 }, { "Three",3 }, };So habe ich es gemacht.
Sanket Sonavane
@SanketSonavane a readonly Dictionary<TKey, TValue>ist nicht gleichbedeutend mit a ReadOnlyDictionary<TKey, TValue>. Tatsächlich ist die Art und Weise, wie sie "schreibgeschützt" sind, ziemlich gegensätzlich. Siehe: dotnetfiddle.net/v0l4aA .
Broots Waymb
2

Warum nicht Namespaces oder Klassen verwenden, um Ihre Werte zu verschachteln? Es mag unvollkommen sein, aber es ist sehr sauber.

public static class ParentClass
{
    // here is the "dictionary" class
    public static class FooDictionary
    {
        public const string Key1 = "somevalue";
        public const string Foobar = "fubar";
    }
}

Jetzt können Sie auf .ParentClass.FooDictionary.Key1 usw. zugreifen.

Joshua
quelle
Da dies die IDataErrorInfo-Schnittstelle nicht implementiert.
David Schmitt
1

Es scheint keine unveränderliche Standardschnittstelle für Wörterbücher zu geben, daher scheint das Erstellen eines Wrappers leider die einzig vernünftige Option zu sein.

Bearbeiten : Marc Gravell hat das ILookup gefunden, das ich verpasst habe - so können Sie zumindest vermeiden, einen neuen Wrapper zu erstellen, obwohl Sie das Wörterbuch noch mit .ToLookup () transformieren müssen.

Wenn dies ein auf ein bestimmtes Szenario beschränktes Bedürfnis ist, sind Sie möglicherweise mit einer stärker auf Geschäftslogik ausgerichteten Schnittstelle besser dran:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}
Sander
quelle
1

Ich bin nicht sicher, warum dies niemand erwähnt hat, aber in C # verwende ich für Dinge, die ich nicht const zuweisen kann, statische schreibgeschützte Eigenschaften.

Beispiel:

public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>()
        {
            { "Reference1", Array1 },
            { "Reference2", Array2 },
            { "Reference3", Array3 },
            { "Reference4", Array4 },
            { "Reference5", Array5 }
        };
Suleman
quelle
weil der Inhalt des Wörterbuchs immer noch veränderbar ist und zur Laufzeit zugewiesen wird.
David Schmitt
0

Nur eine weitere Idee, da ich an eine Winforms-Combobox binde:

public enum DateRange {
    [Display(Name = "None")]
    None = 0,
    [Display(Name = "Today")]
    Today = 1,
    [Display(Name = "Tomorrow")]
    Tomorrow = 2,
    [Display(Name = "Yesterday")]
    Yesterday = 3,
    [Display(Name = "Last 7 Days")]
    LastSeven = 4,
    [Display(Name = "Custom")]
    Custom = 99
    };

int something = (int)DateRange.None;

So erhalten Sie den int-Wert aus dem Anzeigenamen von :

public static class EnumHelper<T>
{
    public static T GetValueFromName(string name)
    {
        var type = typeof(T);
        if (!type.IsEnum) throw new InvalidOperationException();

        foreach (var field in type.GetFields())
        {
            var attribute = Attribute.GetCustomAttribute(field,
                typeof(DisplayAttribute)) as DisplayAttribute;
            if (attribute != null)
            {
                if (attribute.Name == name)
                {
                    return (T)field.GetValue(null);
                }
            }
            else
            {
                if (field.Name == name)
                    return (T)field.GetValue(null);
            }
        }

        throw new ArgumentOutOfRangeException("name");
    }
}

Verwendung:

var z = (int)EnumHelper<DateRange>.GetValueFromName("Last 7 Days");
Gina Marano
quelle
-2

Warum nicht:

public class MyClass
{
    private Dictionary<string, int> _myCollection = new Dictionary<string, int>() { { "A", 1 }, { "B", 2 }, { "C", 3 } };

    public IEnumerable<KeyValuePair<string,int>> MyCollection
    {
        get { return _myCollection.AsEnumerable<KeyValuePair<string, int>>(); }
    }
}

quelle
3
Weil dies im Vergleich zu den verfügbaren Alternativen unter den angegebenen Bedingungen ziemlich teuer ist.
David Schmitt
Auch weil dies nicht einmal kompiliert wird
AustinWBryan
@AustinWBryan ja es kompiliert
derHugo