Créer un dictionnaire constant en C #

128

Quelle est la manière la plus efficace de créer un mappage constant (ne change jamais à l'exécution) de strings à ints?

J'ai essayé d'utiliser un dictionnaire const , mais cela n'a pas fonctionné.

Je pourrais implémenter un wrapper immuable avec une sémantique appropriée, mais cela ne semble toujours pas tout à fait correct.


Pour ceux qui ont demandé, j'implémente IDataErrorInfo dans une classe générée et je cherche un moyen d'effectuer la recherche columnName dans mon tableau de descripteurs.

Je n'étais pas au courant (faute de frappe lors du test! D'oh!) Que le commutateur accepte les chaînes, c'est donc ce que je vais utiliser. Merci!

David Schmitt
la source
1
il y a une solution ici: stackoverflow.com/questions/10066708/…

Réponses:

180

La création d'un dictionnaire de constantes réellement généré au moment de la compilation en C # n'est pas vraiment une tâche simple. En fait, aucune des réponses ici n'y parvient vraiment.

Il existe cependant une solution qui répond à vos besoins, mais pas nécessairement une solution intéressante; rappelez-vous que selon la spécification C #, les tables de commutation sont compilées en tables de sauts de hachage constant. Autrement dit, ce sont des dictionnaires constants, pas une série d'instructions if-else. Considérons donc une instruction switch-case comme celle-ci:

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

C'est exactement ce que vous voulez. Et oui, je sais, c'est moche.

Tamas Czinege
la source
9
C'est du code généré de toute façon, a de bonnes caractéristiques de performance, peut être calculé au moment de la compilation et a également d'autres propriétés intéressantes pour mon cas d'utilisation. Je vais le faire comme ça!
David Schmitt
4
veillez simplement à ce que le retour de types sans valeur, même comme celui-ci, rompe l'immuabilité de vos classes.
Tom Anderson
35
switch-case est génial, jusqu'à ce que vous ayez besoin de "foreach" à travers les valeurs.
Alex
6
Il manque beaucoup de fonctions intéressantes que les dictionnaires ont.
Zinan Xing
1
@TomAnderson: La construction se comportera comme immuable si les éléments retournés sont des types valeur (mutables ou non), ou s'ils sont des objets de classe immuables.
supercat
37

Il existe quelques précieuses collections immuables dans le cadre actuel. Je peux penser à une option relativement simple dans .NET 3.5:

Utilisation Enumerable.ToLookup()- la Lookup<,>classe est immuable (mais à valeurs multiples sur les rhs); vous pouvez le faire Dictionary<,>assez facilement:

    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
la source
15
enum Constants
{
    Abc = 1,
    Def = 2,
    Ghi = 3
}

...

int i = (int)Enum.Parse(typeof(Constants), "Def");
Richard Poole
la source
2
Idée intéressante! Je me demande à quel point l'appel Parse () fonctionne. Je crains que seul un profileur puisse répondre à cette question.
David Schmitt
10

Voici la chose la plus proche que vous puissiez obtenir dans un "Dictionnaire CONST":

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

Le compilateur sera suffisamment intelligent pour créer le code aussi propre que possible.

Timothy Khouri
la source
8

Si vous utilisez 4.5+ Framework, j'utiliserais ReadOnlyDictionary (également ReadOnly Collection pour les listes) pour faire des mappages / constantes en lecture seule. Il est mis en œuvre de la manière suivante.

static class SomeClass
{
    static readonly ReadOnlyDictionary<string,int> SOME_MAPPING 
        = new ReadOnlyDictionary<string,int>(
            new Dictionary<string,int>()
            {
                { "One", 1 },
                { "Two", 2 }
            }
        )
}        
Kram
la source
private static readonly Dictionary<string, string> _yourDictionaryName = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase) { { "One",1 }, { "Two",2 }, { "Three",3 }, };Voilà comment je l'ai fait.
Sanket Sonavane
@SanketSonavane a readonly Dictionary<TKey, TValue>n'est pas équivalent à a ReadOnlyDictionary<TKey, TValue>. En fait, la manière dont ils sont «en lecture seule» est assez opposée. Voir: dotnetfiddle.net/v0l4aA .
Broots Waymb
2

Pourquoi ne pas utiliser des espaces de noms ou des classes pour imbriquer vos valeurs? C'est peut-être imparfait, mais c'est très propre.

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

Vous pouvez maintenant accéder à .ParentClass.FooDictionary.Key1, etc.

Joshua
la source
Parce que cela n'implémente pas l'interface IDataErrorInfo.
David Schmitt
1

Il ne semble pas y avoir d'interface immuable standard pour les dictionnaires, donc la création d'un wrapper semble être la seule option raisonnable, malheureusement.

Edit : Marc Gravell a trouvé le ILookup que j'ai manqué - cela vous permettra au moins d'éviter de créer un nouveau wrapper, même si vous devez encore transformer le dictionnaire avec .ToLookup ().

S'il s'agit d'un besoin limité à un scénario spécifique, vous pourriez être mieux avec une interface plus orientée vers la logique métier:

interface IActiveUserCountProvider
{
    int GetMaxForServer(string serverName);
}
Ponceuse
la source
1

Je ne sais pas pourquoi personne n'a mentionné cela, mais en C # pour les choses que je ne peux pas attribuer à const, j'utilise des propriétés statiques en lecture seule.

Exemple:

public static readonly Dictionary<string, string[]> NewDictionary = new Dictionary<string, string[]>()
        {
            { "Reference1", Array1 },
            { "Reference2", Array2 },
            { "Reference3", Array3 },
            { "Reference4", Array4 },
            { "Reference5", Array5 }
        };
Suleman
la source
car le contenu du dictionnaire est toujours modifiable et il est alloué au moment de l'exécution.
David Schmitt
0

Juste une autre idée puisque je suis lié à une combobox winforms:

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;

Pour obtenir la valeur int à partir du nom d'affichage à partir de :

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");
    }
}

usage:

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

Pourquoi pas:

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>>(); }
    }
}

la source
3
Parce que c'est assez cher par rapport aux alternatives disponibles dans les conditions spécifiées.
David Schmitt
Aussi parce que cela ne compile même pas
AustinWBryan
@AustinWBryan oui il compile
derHugo