7
votes

En C #, comment cartographier facilement les drapeaux Enum d'un type à un autre?

Voir aussi les mises à jour à la fin de la question ... strong>

Compte tenu de la situation suivante: p> xxx pré>

Je voudrais Convertissez un type Enum à l'autre et vice-versa basé sur la fonction de mappage à l'aide des noms tels qu'un grossiot () mais depuis que cela a un drapeau enum, je concevant une telle routine pour être générique. P>

Fondamentalement, ce que je veux, c'est quelque chose comme ce qui suit: p>

exemple # 1 p> xxx pré>

exemple # 2 p> xxx Pré>

Exemple # 3 P> xxx pré>

exemple # 4 p> xxx pré>

exemple # 5 p>

enum SourceEnum
{
    SA = 0x08,
    SB = 0x20,
    SC = 0x10,
    SAB = SA | SB,
    SABC = SA | SB | SC,
}

enum DestEnum
{
    DA = 0x04,
    DB = 0x80,
    DC = 0x01,
    DAB = DA | DB,
}
  • sourceenum.sa mappé à destinum.da li>
  • sourceenum.sb mappé sur destinum.db li>
  • sourceenum.sc mappé sur destinum.dc li> ul>

    et le destin de résultat serait: destinum.da | Destinum.db | Destinum.dc p> p>


0 commentaires

7 Réponses :


0
votes

Je ne sais pas pourquoi vous devez faire cela, car il semble que vous ayez vraiment besoin d'une énumération.

S'il est prudent de supposer que les valeurs numériques des valeurs "équivalentes" enum sont toujours identiques, alors la question réelle est "la valeur fournie a-t-elle des" drapeaux "qui ne font pas partie de l'énumération cible" . Une façon de le faire serait de boucler toutes les valeurs possibles pour l'énumération cible. Si la FLAS est définie, alors Xor de la valeur. Si la valeur! = 0 à la fin de la boucle, il ne peut pas être converti.

Si cela peut être converti, alors jetez la valeur sur un int, puis sur le nouveau type.

ps. Ai-je mentionné qu'il est étrange de faire cela en premier lieu?


2 commentaires

Cela peut sembler étrange, mais la raison d'une telle fonction est de cartographier entre un énorme d'une autre assemblée à une énumération définie dans ma propre assemblée. De plus, les valeurs réelles ne sont pas nécessairement les mêmes d'une enum à une autre. Seule une cartographie «manuelle» avec une certaine aide ferait.


@Stecy, c'est le genre de détail que vous devriez inclure dans la question.



-1
votes

Le dictionnaire générique est implémenté comme une haperttable, par conséquent, la complexité de l'algorithme est O (1). Donc, si Enum plutôt grand, c'est un moyen le plus rapide.

édité: strong> Pour clarifier ... suppose que vous avez plusieurs délégués qui déclare la règle de conversion, où l'un d'entre eux est par défaut (SA-> DA), Name: défaut_délégate. P>

class MyMapper
{
    delegate DestEnum singlemap(SourceEnum);
    static Dictionary<SourceEnum, singlemap> _source2dest = 
       new Dictionary<SourceEnum, singlemap>();
    static MyMapper()
    {
         //place there non-default conversion
         _source2dest[S_xxxx] = My_delegate_to_cast_S_xxxx;
         ......
    }
    static singlemap MapDelegate(SourceEnum se)
    {
        singlemap retval;
        //checking has complexity O(1)
        if(_source2dest.TryGetValue ( se, out retval) )
            return retval;
        return default_delegate;
    }


3 commentaires

Je ne suis pas sûr si je comprends votre réponse ... pourriez-vous élaborer?


Cela ne fonctionne pas dans ce cas, sauf si vous êtes prêt à ajouter chaque combinaison des drapeaux dans le dictionnaire


@Stecy - J'ai mis à jour le message avec exemple (désolé, il contient des erreurs de syntaxe)



1
votes

Si les valeurs de votre Enums sont logiquement équivalentes, vous pouvez simplement lancer une enum à l'autre, par exemple

public DestEnum Map(SourceEnum source) {
    return (DestEnum)SourceEnum;
}


1 commentaires

Exactement, j'aurais besoin d'une cartographie personnalisée. Cependant, je regarde une façon de le faire de manière générique de manière générique ...



0
votes
  public ReturnEnum ConvertEnum<InEnum, ReturnEnum>(InEnum fromEnum)
  {
     ReturnEnum ret = (ReturnEnum)Enum.ToObject(typeof(ReturnEnum), fromEnum);
     if (Enum.IsDefined(typeof(ReturnEnum), ret))
     {
        return ret;
     }
     else
     {
        throw new Exception("Nope"); // TODO: Create more informative error here
     }
  }

3 commentaires

Ne fonctionnera pas depuis par exemple SA devrait carte vers DA, qui a une valeur différente.


@Havard - Quoi? Cela changera SA à DA car ils ont la même valeur, vérifiez ses énormes initiales. S'ils sont différents, cela lancera une exception comme s'il voulait


Voir la clarification. On souhaite que la carte. SA à da, mais ils ne partagent pas nécessairement des valeurs.



1
votes

Je pense que quelque chose le long de ces lignes fonctionnerait, en supposant que les noms des énums suivent un modèle similaire:

DestEnum de = DestEnum.DNone;
        SourceEnum se = SourceEnum.SA;
        Dictionary<int, int> maps = new Dictionary<int, int>();
        maps.Add((int)SourceEnum.SNone, (int)DestEnum.DNone);
        maps.Add((int)SourceEnum.SAB, (int)(DestEnum.DA | DestEnum.DB));
        maps.Add((int)SourceEnum.SA, (int)DestEnum.DA);
        de = (DestEnum)maps[(int)se];


2 commentaires

Eh bien, si vous ne pouvez pas la baser sur le nom ou la valeur, vous devez simplement les cartographier manuellement.


À ce stade, je pense que vous êtes dans la cartographie manuelle. Mais en utilisant un dictionnaire d'INTS semble être la meilleure option ..



3
votes

Voici une solution qui prend simplement un dictionnaire de mappages et effectue une cartographie en balayant dessus. Malheureusement, System.Enum ne peut pas être utilisé comme contrainte générique, donc j'ai construit la solution à l'aide d'une classe dérivée spécifique qui gère la coulée.

Notez que le constructeur de Flagmapper prend des paires de drapeaux simples qui correspondent à l'autre. Il peut également cartographier plusieurs bits sur plusieurs bits aussi longtemps que vous vous assurez que les mappages sont tous cohérents. Si tous les bits du premier élément de la paire sont allumés dans la source Enum, les bits du deuxième élément de la paire seront définis dans l'énum de destination. P>

la cartographie pour SLLA Il ne fonctionne actuellement pas parce que dans mon constructeur, je n'ai pas cartographié les bits d'ordre supérieur. Je n'ai pas fait cette cartographie car il est un peu incompatible avec l'obligation que la cartographie de SD échoue. P>

using System;
using System.Collections.Generic;

namespace Flags
{
    [Flags]
    enum SourceEnum
    {
        SNone = 0x00,

        SA = 0x01,
        SB = 0x02,
        SC = 0x04,
        SD = 0x08,

        SAB = SA | SB,

        SALL = -1,
    }

    [Flags]
    enum DestEnum
    {
        DNone = 0x00,

        DA = 0x01,
        DB = 0x02,
        DC = 0x04,

        DALL = 0xFF,
    }

    class FlagMapper
    {
        protected Dictionary<int, int> mForwardMapping;

        protected FlagMapper(Dictionary<int, int> mappings)
        {
            this.mForwardMapping = mappings;
        }

        protected int Map(int a)
        {
            int result = 0;

            foreach (KeyValuePair<int, int> mapping in this.mForwardMapping)
            {
                if ((a & mapping.Key) == mapping.Key)
                {
                    if (mapping.Value < 0)
                    {
                        throw new Exception("Cannot map");
                    }

                    result |= mapping.Value;
                }
            }

            return result;
        }
    }

    class SourceDestMapper : FlagMapper
    {
        public SourceDestMapper()
            : base(new Dictionary<int, int>
            {
                { (int)SourceEnum.SA, (int)DestEnum.DA },
                { (int)SourceEnum.SB, (int)DestEnum.DB },
                { (int)SourceEnum.SC, (int)DestEnum.DC },
                { (int)SourceEnum.SD, -1 }
            })
        {
        }

        public DestEnum Map(SourceEnum source)
        {
            return (DestEnum)this.Map((int)source);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SourceDestMapper mapper = new SourceDestMapper();

            Console.WriteLine(mapper.Map(SourceEnum.SA));
            Console.WriteLine(mapper.Map(SourceEnum.SA | SourceEnum.SB));
            Console.WriteLine(mapper.Map(SourceEnum.SAB));

            //Console.WriteLine(mapper.Map(SourceEnum.SALL));

            Console.WriteLine(mapper.Map(SourceEnum.SD));
        }
    }
}


3 commentaires

Fermer ... La seule chose qui manque, comme vous l'avez écrit, est de gérer le SLL pour faire la cartographie.


Le mappage de Sall to Dally devrait fonctionner correctement en ajoutant {(int) sourceenum.sall, (int) destinum.dall} . Cependant, vous devrez supprimer le mappage d'échec pour SD ou définir Sall sans le bit SD.


En fait, il peut être préférable de gérer des mappages d'échec d'une manière différente. Sans les mappages d'échec dans le dictionnaire, le même dictionnaire pourrait être utilisé pour les mappages avant et inverse.



4
votes

Vous pouvez également utiliser des méthodes d'extension pour vous transformer sourceenum vers destinum, voici le code avec des tests de l'unité

ou utilisez un autre excellent outil tel que ValueInjectorTer: http://valueinjectateur.codeplex.com/ xxx


0 commentaires