web-dev-qa-db-fra.com

Pouvez-vous ajouter un type enum au moment de l'exécution

Si j'ai le type enum:

Public enum Sport
{
    Tennis = 0;
    Football = 1;
    Squash = 2;
    Volleyball = 3;
}

Puis-je en quelque sorte ajouter pendant l'exécution:

PingPong = 4
25
CJ7

L'énumération a un magasin de sauvegarde, par défaut sur int si vous ne le spécifiez pas. Il est possible d'affecter directement des valeurs en dehors des valeurs définies:

Sport pingPong = (Sport)4;

Ensuite, vous pouvez le vérifier:

if (value == (Sport)4) {}

C'est pourquoi vous avez la fonction statique Enum.IsDefined() pour vérifier si la valeur réelle se situe dans les valeurs attendues. Notez que la fonction ne fonctionne pas pour les valeurs d'indicateur composé.

bool isValueDefined = Enum.IsDefined(typeof(Sport), value);

EDIT: Après le commentaire de Hans Passant: Vous n'avez pas à utiliser la valeur littérale 4. Vous pouvez utiliser tout ce qui retourne un int. Par exemple:

Dictionary<int, string> AdditionalSports = new Dictionary<int, string>();
AdditionalSports.Add(4, "PingPong");

// Usages: if
if (AdditionalSports.ContainsKey(value))
{
    // Maybe do something with AdditionalSports[value], i.e. "PingPong"
}

// In a switch:
switch (value)
{
case default:
    // Since it won't be found in the enum-defined values
    if (AdditionalSports.ContainsKey(value))
    {
        // Maybe do something with AdditionalSports[value], i.e. "PingPong"
    }
}
25
Daniel Rose

Voici un moyen plus orienté objet pour peut-être atteindre ce que vous essayez d'atteindre. Cette solution s’inspire de la première approche d’énumération en Java:

struct Sport {
    readonly int value;
    public Sport(int value) {
        this.value = value;
    }
    public static implicit operator int(Sport sport) {
        return sport.value;
    }
    public static implicit operator Sport(int sport) {
        return new Sport(sport);
    }

    public const int Tennis =       0;
    public const int Football =     1;
    public const int Squash =       2;
    public const int Volleyball =   3;
}

//Usage:
Sport sport = Sport.Volleyball;
switch(sport) {
    case Sport.Squash:
        Console.WriteLine("I bounce really high");
        break;
}
Sport rugby = 5;
if (sport == rugby)
    Console.WriteLine("I am really big and eat a lot");

Pour parcourir différentes fonctionnalités de cette solution.

  1. C'est une structure immuable qui encapsule une valeur entière. La valeur est imposée de manière immuable par le mot clé readonly.

  2. La seule façon de créer l'une de ces structures est d'appeler le constructeur qui prend la valeur en tant que paramètre.

  3. implicit operator int est là pour que la structure puisse être utilisée dans le switch bock - c’est-à-dire pour rendre la structure convertible en int.

  4. implicit operator Sport est là pour que vous puissiez affecter des valeurs entières à la structure, c'est-à-dire Sport rugby = 5.

  5. Les valeurs const sont les sports connus au moment de la compilation. Ils peuvent également être utilisés comme étiquettes case

Ce que je ferais réellement

public static class Sports {
    public static readonly Sport Football = new Sport("Football");
    public static readonly Sport Tennis = new Sport("Tennis");
}

public class Sport {
    public Sport(string name) {
        Name = name;
    }
    public string Name { get; private set; }

    // override object.Equals
    public override bool Equals(object obj) {
        var other = obj as Sport;
        if(other == null) {
            return false;
        }

        return other == this;
    }

    // override object.GetHashCode
    public override int GetHashCode() {
        return Name.GetHashCode();
    }

    public static bool operator == (Sport sport1, Sport sport2) {
        if(Object.ReferenceEquals(sport1, null) && Object.ReferenceEquals(sport2 , null))
            return true;

        if(Object.ReferenceEquals(sport1, null) || Object.ReferenceEquals(sport2, null))
            return false;

        return sport1.Name == sport2.Name;
    }
    public static bool operator !=(Sport sport1, Sport sport2) {
        return !(sport1 == sport2);
    }
}

Cela créerait une classe de valeur Sport qui aurait un nom. En fonction de votre application, vous pouvez étendre cette classe pour fournir d'autres attributs et méthodes. Avoir cela comme classe vous donne plus de flexibilité puisque vous pouvez le sous-classer.

La classe Sports fournit une collection statique de sports connus au moment de la compilation. Ceci est similaire à la manière dont certains frameworks .NET traitent les couleurs nommées (à savoir WPF). Voici l'usage:

List<Sport> sports = new List<Sport>();

sports.Add(Sports.Football);
sports.Add(Sports.Tennis);
//What if the name contains spaces?
sports.Add(new Sport("Water Polo"));

var otherSport = new Sport("Other sport");

if(sports.Contains(otherSport)) {
    //Do something
}

foreach(var sport in sports) {
    if(sport == otherSport) {
        //Do Something
    } else if(sport == Sports.Football) {
        //do something else
    }
}

Une fois que vous avez fait cela, vous vous rendrez compte qu’il était très peu nécessaire d’avoir une énumération, car toutes les opérations conditionnelles sur le type de sport peuvent être traitées dans la classe Sport.

EDITJ'ai réalisé que mon opérateur d'égalité lancera une StackOverflowException. J'oublie toujours d'écrire Object.ReferenceEquals(obj,null) au lieu de obj==null, ce qui entraînera une récurrence indéfinie.

9
Igor Zevaka

Non, vous ne pouvez pas modifier les types au moment de l'exécution. Vous pouvez émettre de nouveaux types, mais modifier ceux existants n'est pas possible.

5
Darin Dimitrov

Eh bien oui, vous pouvez créer et/ou modifier un Enum au moment de l'exécution à l'aide de la classe EnumBuilder . Voir l'exemple dans MSDN

ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
EnumBuilder eb = mb.DefineEnum("Elevation", TypeAttributes.Public, typeof(int));
eb.DefineLiteral("Low", 0);
eb.DefineLiteral("High", 1);
Type finished = eb.CreateType();

À mon avis, avoir une valeur enum est préférable aux dictionnaires ou aux solutions basées sur des listes, car on utilise moins de mémoire et aucun effet secondaire lié aux threads.

Voici quelques exemples sur la façon de charger l’énum généré lorsque vous redémarrez votre application. Je vous suggère de choisir celui qui fonctionne pour vous, vous pourriez utiliser les méthodes fournies par le System.AddIn espace de noms que je suppose .. ou utilisez votre IoC.

J'utilise ceci lorsque j'ai besoin de générer des données pour Machine learning avec une valeur Enum et sont préférables aux tables de consultation (en mémoire de base de données) car je ne dispose pas du IO avec ces grands ensembles de données.

Code heureux

Walter

Si l'énumération peut être définie au démarrage du programme, placez-la dans un assembly séparé et utilisez un programme d'amorçage qui recompilera l'énum, ​​en écrasant l'ancienne version, puis en lançant l'application. Ce n'est pas la méthode la plus propre, mais ça marche.

0
Stephan

Je créerais un Enum complet, avec toutes les valeurs dont vous aurez besoin comme ça

Public enum Sport
{
    Tennis = 0;
    Football = 1;
    Squash = 2;
    Volleyball = 3;
    PingPong = 4;
    Rugby = 5;  // for example
}

Et gardez ensuite une LISTE des entrées de sport non valides afin que la liste contienne initialement PingPong et Rugby. Chaque fois que vous accédez à Enum, vérifiez également avec votre liste de sports non valides.

Ensuite, vous pouvez simplement ajuster votre liste de sports non valide en fonction de vos besoins, à tout moment.

0
Grantly