[c#] Enum dynamique en C #


Answers

Vous réalisez que les Enums doivent être spécifiés au moment de la compilation? Vous ne pouvez pas ajouter dynamiquement des énumérations pendant l'exécution - et pourquoi voudriez-vous, il n'y aurait aucune utilisation / référence à eux dans le code?

De Professional C # 2008:

Le véritable pouvoir des énumérations en C # est que, dans les coulisses, elles sont instanciées en tant que structures dérivées de la classe de base, System.Enum. Cela signifie qu'il est possible d'appeler des méthodes contre eux pour effectuer certaines tâches utiles. Notez qu'en raison de la façon dont le .NET Framework est implémenté, il n'y a pas de perte de performance associée au traitement syntaxique des enum en structs. En pratique, une fois votre code compilé, les énumérations existeront en tant que types primitifs, tout comme int et float.

Donc, je ne suis pas sûr que vous pouvez utiliser Enums comme vous le souhaitez.

Question

Comment puis-je créer une énumération dynamique (et ensuite utiliser les choix d'énumération) en C # en fonction des valeurs d'une table de recherche de base de données (en utilisant la couche de données de la bibliothèque d'entreprise)?

Par exemple, si j'ajoute une nouvelle valeur de recherche dans la base de données, je ne souhaite pas ajouter la déclaration de valeur enum statique supplémentaire dans le code.

Y a-t-il une chose pareille? Je ne veux pas créer une énumération statique générée par code (comme dans l'article The Code Project Générateur de code Enum - Génération de code enum automatiquement à partir des tables de consultation de base de données ) et préférerait que ce soit complètement dynamique.




J'aime toujours écrire mon propre "enum personnalisé". Que j'ai un cours un peu plus complexe, mais je peux le réutiliser:

public abstract class CustomEnum
{
    private readonly string _name;
    private readonly object _id;

    protected CustomEnum( string name, object id )
    {
        _name = name;
        _id = id;
    }

    public string Name
    {
        get { return _name; }
    }

    public object Id
    {
        get { return _id; }
    }

    public override string ToString()
    {
        return _name;
    }
}

public abstract class CustomEnum<TEnumType, TIdType> : CustomEnum
    where TEnumType : CustomEnum<TEnumType, TIdType>
{
    protected CustomEnum( string name, TIdType id )
        : base( name, id )
    { }

    public new TIdType Id
    {
        get { return (TIdType)base.Id; }
    }

    public static TEnumType FromName( string name )
    {
        try
        {
            return FromDelegate( entry => entry.Name.Equals( name ) );
        }
        catch (ArgumentException ae)
        {
            throw new ArgumentException( "Illegal name for custom enum '" + typeof( TEnumType ).Name + "'", ae );
        }
    }

    public static TEnumType FromId( TIdType id )
    {
        try
        {
            return FromDelegate( entry => entry.Id.Equals( id ) );
        }
        catch (ArgumentException ae)
        {
            throw new ArgumentException( "Illegal id for custom enum '" + typeof( TEnumType ).Name + "'", ae );
        }
    }

    public static IEnumerable<TEnumType> GetAll()
    {
        var elements = new Collection<TEnumType>();
        var infoArray = typeof( TEnumType ).GetFields( BindingFlags.Public | BindingFlags.Static );

        foreach (var info in infoArray)
        {
            var type = info.GetValue( null ) as TEnumType;
            elements.Add( type );
        }

        return elements;
    }

    protected static TEnumType FromDelegate( Predicate<TEnumType> predicate )
    {
        if(predicate == null)
            throw new ArgumentNullException( "predicate" );

        foreach (var entry in GetAll())
        {
            if (predicate( entry ))
                return entry;
        }

        throw new ArgumentException( "Element not found while using predicate" );
    }
}

Maintenant j'ai juste besoin de créer mon énumération que je veux utiliser:

 public sealed class SampleEnum : CustomEnum<SampleEnum, int>
    {
        public static readonly SampleEnum Element1 = new SampleEnum( "Element1", 1, "foo" );
        public static readonly SampleEnum Element2 = new SampleEnum( "Element2", 2, "bar" );

        private SampleEnum( string name, int id, string additionalText )
            : base( name, id )
        {
            AdditionalText = additionalText;
        }

        public string AdditionalText { get; private set; }
    }

Enfin je peux l'utiliser comme je veux:

 static void Main( string[] args )
        {
            foreach (var element in SampleEnum.GetAll())
            {
                Console.WriteLine( "{0}: {1}", element, element.AdditionalText );
                Console.WriteLine( "Is 'Element2': {0}", element == SampleEnum.Element2 );
                Console.WriteLine();
            }

            Console.ReadKey();
        }

Et ma sortie serait:

Element1: foo
Is 'Element2': False

Element2: bar
Is 'Element2': True    



Je ne pense pas qu'il y ait un bon moyen de faire ce que vous voulez. Et si vous y réfléchissez, je ne pense pas que ce soit ce que vous voulez vraiment.

Si vous voulez une énumération dynamique, cela signifie également que vous devez l'alimenter avec une valeur dynamique lorsque vous la référencez. Peut-être avec beaucoup de magie, vous pourriez obtenir une sorte d' IntelliSense qui prendrait soin de cela et générerait une énumération pour vous dans un fichier DLL. Mais considérons la quantité de travail qu'il faudrait, comment il serait inefficace d'accéder à la base de données pour récupérer des informations IntelliSense ainsi que le cauchemar de la version contrôlant le fichier DLL généré.

Si vous ne voulez vraiment pas ajouter manuellement les valeurs enum (vous devrez quand même les ajouter à la base de données), utilisez plutôt un outil de génération de code, par exemple des templates T4 . Cliquez avec le bouton droit de la souris sur + run et votre code est défini statiquement dans le code et vous obtenez tous les avantages de l'utilisation des enums.




Je l'ai fait avec un gabarit T4 . Il est assez trivial de déposer un fichier .tt dans votre projet et de configurer Visual Studio pour qu'il exécute le modèle T4 en tant qu'étape de préconfiguration.

Le T4 génère un fichier .cs, ce qui signifie que vous pouvez l'interroger simplement et créer une énumération dans un fichier .cs à partir du résultat. Câblé comme une tâche de pré-construction, il recréerait votre énumération à chaque génération, ou vous pouvez exécuter le T4 manuellement si nécessaire.




Juste en montrant la answer de Pandincus avec le code "du rayon" et quelques explications: Vous avez besoin de deux solutions pour cet exemple (je sais que cela pourrait se faire via un aussi;), laissez les étudiants avancés le présenter ...

Voici donc le DDL SQL pour la table:

USE [ocms_dev]
    GO

CREATE TABLE [dbo].[Role](
    [RoleId] [int] IDENTITY(1,1) NOT NULL,
    [RoleName] [varchar](50) NULL
) ON [PRIMARY]

Donc, voici le programme de la console produisant la DLL:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Data.Common;
using System.Data;
using System.Data.SqlClient;

namespace DynamicEnums
{
    class EnumCreator
    {
        // after running for first time rename this method to Main1
        static void Main ()
        {
            string strAssemblyName = "MyEnums";
            bool flagFileExists = System.IO.File.Exists (
                   AppDomain.CurrentDomain.SetupInformation.ApplicationBase + 
                   strAssemblyName + ".dll"
            );

            // Get the current application domain for the current thread
            AppDomain currentDomain = AppDomain.CurrentDomain;

            // Create a dynamic assembly in the current application domain,
            // and allow it to be executed and saved to disk.
            AssemblyName name = new AssemblyName ( strAssemblyName );
            AssemblyBuilder assemblyBuilder = 
                    currentDomain.DefineDynamicAssembly ( name,
                            AssemblyBuilderAccess.RunAndSave );

            // Define a dynamic module in "MyEnums" assembly.
            // For a single-module assembly, the module has the same name as
            // the assembly.
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule (
                    name.Name, name.Name + ".dll" );

            // Define a public enumeration with the name "MyEnum" and
            // an underlying type of Integer.
            EnumBuilder myEnum = moduleBuilder.DefineEnum (
                    "EnumeratedTypes.MyEnum",
                    TypeAttributes.Public,
                    typeof ( int )
            );

            #region GetTheDataFromTheDatabase
            DataTable tableData = new DataTable ( "enumSourceDataTable" );

            string connectionString = "Integrated Security=SSPI;Persist " +
                    "Security Info=False;Initial Catalog=ocms_dev;Data " +
                    "Source=ysg";

            using (SqlConnection connection = 
                    new SqlConnection ( connectionString ))
            {

                SqlCommand command = connection.CreateCommand ();
                command.CommandText = string.Format ( "SELECT [RoleId], " + 
                        "[RoleName] FROM [ocms_dev].[dbo].[Role]" );

                Console.WriteLine ( "command.CommandText is " + 
                        command.CommandText );

                connection.Open ();
                tableData.Load ( command.ExecuteReader ( 
                        CommandBehavior.CloseConnection
                ) );
            } //eof using

            foreach (DataRow dr in tableData.Rows)
            {
                myEnum.DefineLiteral ( dr[1].ToString (),
                        Convert.ToInt32 ( dr[0].ToString () ) );
            }
            #endregion GetTheDataFromTheDatabase

            // Create the enum
            myEnum.CreateType ();

            // Finally, save the assembly
            assemblyBuilder.Save ( name.Name + ".dll" );
        } //eof Main 
    } //eof Program
} //eof namespace 

Voici la programmation de la console imprimant la sortie (rappelez-vous qu'elle doit référencer la DLL). Laissez les étudiants avancés présenter la solution pour combiner tout dans une solution avec un chargement dynamique et vérifier s'il y a déjà une DLL de construction.

// add the reference to the newly generated dll
use MyEnums ; 

class Program
{
    static void Main ()
    {
        Array values = Enum.GetValues ( typeof ( EnumeratedTypes.MyEnum ) );

        foreach (EnumeratedTypes.MyEnum val in values)
        {
            Console.WriteLine ( String.Format ( "{0}: {1}",
                    Enum.GetName ( typeof ( EnumeratedTypes.MyEnum ), val ),
                    val ) );
        }

        Console.WriteLine ( "Hit enter to exit " );
        Console.ReadLine ();
    } //eof Main 
} //eof Program



Une façon de conserver les énumérations et de créer une liste dynamique de valeurs en même temps consiste à utiliser les énumérations que vous avez actuellement avec un dictionnaire créé dynamiquement.

Comme la plupart des Enums sont utilisés dans le contexte pour lequel ils sont définis, et que les «Enum dynamiques» seront pris en charge par des processus dynamiques, vous pouvez distinguer les 2.

La première étape consiste à créer une table / collection contenant les ID et les références pour les entrées dynamiques. Dans le tableau, vous allez auto-incrémenter beaucoup plus que votre plus grande valeur Enum.

Maintenant vient la partie pour vos Enums dynamiques, je suppose que vous utiliserez les Enums pour créer un ensemble de conditions qui appliquent un ensemble de règles, dont certaines sont générées dynamiquement.

Get integer from database
If Integer is in Enum -> create Enum -> then run Enum parts
If Integer is not a Enum -> create Dictionary from Table -> then run Dictionary parts.



Links