Comment passez-vous plusieurs valeurs d'énumération en C #?

117

Parfois, lors de la lecture du code C # des autres, je vois une méthode qui acceptera plusieurs valeurs d'énumération dans un seul paramètre. J'ai toujours pensé que c'était plutôt chouette, mais je ne l'ai jamais regardé.

Eh bien, maintenant je pense que j'en ai peut-être besoin, mais je ne sais pas comment

  1. configurer la signature de méthode pour accepter cela
  2. travailler avec les valeurs de la méthode
  3. définir l'énumération

pour réaliser ce genre de chose.


Dans ma situation particulière, j'aimerais utiliser le System.DayOfWeek, qui est défini comme:

[Serializable]
[ComVisible(true)]
public enum DayOfWeek
{ 
    Sunday = 0,   
    Monday = 1,   
    Tuesday = 2,   
    Wednesday = 3,   
    Thursday = 4,   
    Friday = 5,    
    Saturday = 6
}

Je souhaite pouvoir transmettre une ou plusieurs des valeurs DayOfWeek à ma méthode. Est-ce que je pourrai utiliser cette énumération particulière telle quelle? Comment faire les 3 choses ci-dessus?

Ronnie Overby
la source
1
Comme mentionné ci-dessous, vous pouvez utiliser une énumération Flags. Vous pouvez consulter les tenants et aboutissants des énumérations C # , qui contient plus d'informations sur l'utilisation des énumérations Flags.
ChaseMedallion

Réponses:

181

Lorsque vous définissez l'énumération, attribuez-la simplement avec [Flags], définissez les valeurs sur des puissances de deux, et cela fonctionnera de cette façon.

Rien d'autre ne change, à part le passage de plusieurs valeurs dans une fonction.

Par exemple:

[Flags]
enum DaysOfWeek
{
   Sunday = 1,
   Monday = 2,
   Tuesday = 4,
   Wednesday = 8,
   Thursday = 16,
   Friday = 32,
   Saturday = 64
}

public void RunOnDays(DaysOfWeek days)
{
   bool isTuesdaySet = (days & DaysOfWeek.Tuesday) == DaysOfWeek.Tuesday;

   if (isTuesdaySet)
      //...
   // Do your work here..
}

public void CallMethodWithTuesdayAndThursday()
{
    this.RunOnDays(DaysOfWeek.Tuesday | DaysOfWeek.Thursday);
}

Pour plus de détails, consultez la documentation de MSDN sur les types d'énumération .


Modifier en réponse aux ajouts à la question.

Vous ne pourrez pas utiliser cette énumération telle quelle, à moins que vous ne vouliez faire quelque chose comme la passer en tant que tableau array / collection / params. Cela vous permettrait de transmettre plusieurs valeurs. La syntaxe des indicateurs nécessite que Enum soit spécifié comme indicateurs (ou pour bâtarder le langage d'une manière qui n'est pas conçue).

Reed Copsey
la source
9
Je pense que si vous voulez que cela fonctionne, les valeurs doivent être définies comme des puissances de deux, un seul bit pour chaque valeur. Ce qui se passe, c'est que vous transmettez le OU au niveau du bit des valeurs du paramètre, que vous pouvez vérifier à l'aide d'opérations AND au niveau du bit. Si vous ne prenez pas soin de définir les valeurs comme, par exemple, 1, 2, 4, 8, etc., vous aurez des problèmes.
Denis Troller
1
La définition de l'attribut Flags seul ne fonctionne pas automatiquement. J'ai juste essayé.
Richard Anthony Hein
1
Vous avez raison, et j'ai supprimé la désinformation. Correction du code de Reed aussi pour lui.
Matthew Scharley
@monoxide: Je l'ai corrigé en même temps - désolé d'avoir annulé vos modifications.
Reed Copsey
2
Pas de problème ... Je préfère toujours utiliser hexadécimal pour des valeurs d'indicateur comme ça, cela me semble plus clair, mais chacun à sa place.
Matthew Scharley
78

Je pense que la solution la plus élégante est d'utiliser HasFlag ():

    [Flags]
    public enum DaysOfWeek
    {
        Sunday = 1,
        Monday = 2,
        Tuesday = 4,
        Wednesday = 8,
        Thursday = 16,
        Friday = 32,
        Saturday = 64
    }

    public void RunOnDays(DaysOfWeek days)
    {
        bool isTuesdaySet = days.HasFlag(DaysOfWeek.Tuesday);

        if (isTuesdaySet)
        {
            //...
        }
    }

    public void CallMethodWithTuesdayAndThursday()
    {
        RunOnDays(DaysOfWeek.Tuesday | DaysOfWeek.Thursday);
    }
Tillito
la source
Il y a une autre chose importante à savoir, si vous le faites de cette façon: cela ajoute beaucoup plus de flexibilité lorsque vous devez remplir votre énumération de manière dynamique. Par exemple: si vous utilisez des cases à cocher pour définir les jours pendant lesquels vous voulez que votre programme s'exécute, et que vous essayez d'accomplir cela de la manière ci-dessus, le code sera illisible. Donc à la place, vous pouvez le faire de cette façon: ... DaysOfWeek days = new DaysOfWeek(); if (cbTuesday.Checked) days |= DaysOfWeek.Tuesday; if (cbWednesday.Checked) days |= DaysOfWeek.Wednesday;...
CoastN
25

Je seconde la réponse de Reed. Cependant, lors de la création de l'énumération, vous devez spécifier les valeurs de chaque membre d'énumération pour créer une sorte de champ de bits. Par exemple:

[Flags]
public enum DaysOfWeek
{
    Sunday = 1,
    Monday = 2,
    Tuesday = 4,
    Wednesday = 8,
    Thursday = 16,
    Friday = 32,
    Saturday = 64,

    None = 0,
    All = Weekdays | Weekend,
    Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday,
    Weekend = Sunday | Saturday,
    // etc.
}
Jacob
la source
1
Cela vous aidera si vous avez besoin d'inspecter une valeur DaysOfWeek, par exemple si vous en lisez une qui est stockée dans une base de données. En outre, il fournit une meilleure documentation aux autres programmeurs, puisque certains énumèrent les jours de la semaine à partir du lundi au lieu du dimanche.
Jacob
3
Pour plus de clarté et de maintenabilité, j'écrirais votre énumération «Tous» comme suit: «Dimanche | Lundi | Mardi | Mercredi | Jeudi | Vendredi | Samedi» ... et utiliser une notation similaire pour «Jours de la semaine» et «Week-end».
Robert Cartaino
C'est très bien si vous voulez créer votre propre énumération, mais pour répondre à la question, vous ne pouvez pas modifier l'énumération DaysOfWeek existante
Robert Paulson
2
Pas "peut vouloir" ... vous DEVEZ. Les comparaisons au niveau du bit l'exigent. Laisser les valeurs non spécifiées ne fonctionnera pas.
Richard Anthony Hein
11

Dans ma situation particulière, je souhaite utiliser le System.DayOfWeek

Vous ne pouvez pas utiliser le System.DayOfWeek comme une [Flags]énumération car vous n'avez aucun contrôle dessus. Si vous souhaitez avoir une méthode qui accepte plusieurs, DayOfWeekvous devrez utiliser le paramsmot - clé

void SetDays(params DayOfWeek[] daysToSet)
{
    if (daysToSet == null || !daysToSet.Any())
        throw new ArgumentNullException("daysToSet");

    foreach (DayOfWeek day in daysToSet)
    {
        // if( day == DayOfWeek.Monday ) etc ....
    }
}

SetDays( DayOfWeek.Monday, DayOfWeek.Sunday );

Sinon, vous pouvez créer votre propre [Flags]énumération comme indiqué par de nombreux autres répondeurs et utiliser des comparaisons au niveau du bit.

Robert Paulson
la source
2
Je ne sais pas pourquoi je n'ai pas pensé aux params. Cela devrait être très utile pour utiliser des énumérations qui ne sont pas des champs de bits.
Ronnie Over du
10
[Flags]
public enum DaysOfWeek
{
  Mon = 1,
  Tue = 2,
  Wed = 4,
  Thur = 8,
  Fri = 16,
  Sat = 32,
  Sun = 64
}

Vous devez spécifier les nombres et les incrémenter comme ceci car il stocke les valeurs de manière bit à bit.

Ensuite, définissez simplement votre méthode pour prendre cette énumération

public void DoSomething(DaysOfWeek day)
{
  ...
}

et pour l'appeler faites quelque chose comme

DoSomething(DaysOfWeek.Mon | DaysOfWeek.Tue) // Both Monday and Tuesday

Pour vérifier si l'une des valeurs d'énumération a été incluse, vérifiez-les à l'aide d'opérations au niveau du bit comme

public void DoSomething(DaysOfWeek day)
{
  if ((day & DaysOfWeek.Mon) == DaysOfWeek.Mon) // Does a bitwise and then compares it to Mondays enum value
  {
    // Monday was passed in
  }
}
Tétraneutron
la source
Ne répond pas à la question. "Dans ma situation particulière, je voudrais utiliser le System.DayOfWeek"
Robert Paulson
Mais c'est exactement ce que je cherchais. Alors merci quand même.
Tillito
3
[Flags]
    public enum DaysOfWeek{


        Sunday = 1 << 0,
        Monday = 1 << 1,
        Tuesday = 1 << 2,
        Wednesday = 1 << 3,
        Thursday = 1 << 4,
        Friday =  1 << 5,
        Saturday =  1 << 6
    }

appeler la méthode dans ce format

MethodName (DaysOfWeek.Tuesday | DaysOfWeek.Thursday);

Implémentez une méthode EnumToArray pour obtenir les options passées

private static void AddEntryToList(DaysOfWeek days, DaysOfWeek match, List<string> dayList, string entryText) {
            if ((days& match) != 0) {
                dayList.Add(entryText);
            }
        }

        internal static string[] EnumToArray(DaysOfWeek days) {
            List<string> verbList = new List<string>();

            AddEntryToList(days, HttpVerbs.Sunday, dayList, "Sunday");
            AddEntryToList(days, HttpVerbs.Monday , dayList, "Monday ");
            ...

            return dayList.ToArray();
        }
Rony
la source
Ne répond pas à la question. "Dans ma situation particulière, je voudrais utiliser le System.DayOfWeek"
Robert Paulson
Merci, Robert, mais vous n'avez vraiment pas besoin de le souligner sur chaque réponse.
Ronnie Over du
@Ronnie - Ce n'est pas ma faute s'il y a tellement de réponses quasi-dupliquées qui n'ont pas vraiment répondu à la question.
Robert Paulson
@Rony - les énumérations ToString () et Enum.Parse () afficheront / analyseront correctement une énumération [Flags] (tant que vous faites bien attention à la gestion des versions de l'énumération)
Robert Paulson
@Robert - Je pense que personne ne vous blâme. Laissez les votes parler.
Ronnie Over du
3

Marquez votre énumération avec l'attribut [Flags]. Assurez-vous également que toutes vos valeurs sont mutuellement exclusives (deux valeurs ne peuvent pas s'additionner) comme 1,2,4,8,16,32,64 dans votre cas

[Flags]
public enum DayOfWeek
{ 
Sunday = 1,   
Monday = 2,   
Tuesday = 4,   
Wednesday = 8,   
Thursday = 16,   
Friday = 32,    
Saturday = 64
}

Lorsque vous avez une méthode qui accepte une énumération DayOfWeek, utilisez l'opérateur binaire ou (|) pour utiliser plusieurs membres ensemble. Par exemple:

MyMethod(DayOfWeek.Sunday|DayOfWeek.Tuesday|DayOfWeek.Friday)

Pour vérifier si le paramètre contient un membre spécifique, utilisez l' opérateur binaire and (&) avec le membre que vous recherchez.

if(arg & DayOfWeek.Sunday == DayOfWeek.Sunday)
Console.WriteLine("Contains Sunday");
statenjason
la source
Ne répond pas à la question. "Dans ma situation particulière, je voudrais utiliser le System.DayOfWeek"
Robert Paulson
2

Reed Copsey a raison et j'ajouterais au message d'origine si je le pouvais, mais je ne peux pas donc je devrai répondre à la place.

Il est dangereux d'utiliser simplement [Flags] sur n'importe quelle ancienne énumération. Je pense que vous devez modifier explicitement les valeurs d'énumération en puissances de deux lorsque vous utilisez des indicateurs, pour éviter les conflits dans les valeurs. Consultez les instructions pour FlagsAttribute et Enum .

Dan
la source
0

Avec l'aide des réponses affichées et celles-ci:

  1. FlagsAttribute Class (Regardez la comparaison de l'utilisation et de la non utilisation de l'attribut [Flags])
  2. Attribut Enum Flags

J'ai l'impression de bien le comprendre.

Merci.

Ronnie Overby
la source
-3

Quelque chose de cette nature devrait montrer ce que vous recherchez:

[Flags]
public enum SomeName
{
    Name1,
    Name2
}

public class SomeClass()
{
    public void SomeMethod(SomeName enumInput)
    {
        ...
    }
}
Andrew Siemer
la source