¿Cómo se pasan varios valores de enumeración en C #?

117

A veces, al leer el código C # de otros, veo un método que acepta múltiples valores de enumeración en un solo parámetro. Siempre pensé que estaba un poco ordenado, pero nunca lo investigué.

Bueno, ahora creo que puedo necesitarlo, pero no sé cómo

  1. configurar la firma del método para aceptar esto
  2. trabajar con los valores en el método
  3. definir la enumeración

para lograr este tipo de cosas.


En mi situación particular, me gustaría usar System.DayOfWeek, que se define como:

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

Quiero poder pasar uno o más de los valores de DayOfWeek a mi método. ¿Podré usar esta enumeración en particular tal como está? ¿Cómo hago las 3 cosas enumeradas anteriormente?

Ronnie Overby
fuente
1
Como se menciona a continuación, puede usar una enumeración Flags. Es posible que desee consultar los entresijos de las enumeraciones de C # , que contiene más información sobre cómo trabajar con las enumeraciones de Flags.
ChaseMedallion

Respuestas:

181

Cuando defina la enumeración, simplemente atribuícela con [Flags], establezca los valores en potencias de dos y funcionará de esta manera.

Nada más cambia, aparte de pasar varios valores a una función.

Por ejemplo:

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

Para obtener más detalles, consulte la documentación de MSDN sobre tipos de enumeración .


Edite en respuesta a las adiciones a la pregunta.

No podrá usar esa enumeración tal como está, a menos que desee hacer algo como pasarla como una matriz / colección / matriz params. Eso le permitiría pasar múltiples valores. La sintaxis de banderas requiere que Enum se especifique como banderas (o bastardizar el lenguaje de una manera que no está diseñada).

Reed Copsey
fuente
9
Creo que si quieres que esto funcione, los valores deben definirse como potencias de dos, un solo bit para cada valor. Lo que sucede es que pasa el OR bit a bit de los valores en el parámetro, que puede verificar usando operaciones AND bit a bit. Si no se ocupa de definir los valores como, por ejemplo, 1, 2, 4, 8, etc., tendrá problemas.
Denis Troller
1
El solo hecho de configurar el atributo Flags no funciona automáticamente. Solo lo probé.
Richard Anthony Hein
1
Tienes razón, y eliminé la información errónea. También arregló el código de Reed para él.
Matthew Scharley
@monoxide: Lo arreglé al mismo tiempo, lamento anular tus ediciones.
Reed Copsey
2
No hay problema ... todavía prefiero usar hexadecimal para valores de bandera como ese, me parece más claro, pero cada uno tiene el suyo.
Matthew Scharley
78

Creo que la solución más elegante es usar 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
fuente
Hay otra cosa importante que debe saber, si lo hace de esta manera: agrega mucha más flexibilidad cuando necesita completar su enumeración dinámicamente. Por ejemplo: si usa casillas de verificación para definir los días en los que desea que se ejecute su programa, y ​​trataría de lograr esto de la forma en que se hizo anteriormente, el código será ilegible. Entonces, en cambio, puede hacerlo de esta manera: ... DaysOfWeek days = new DaysOfWeek(); if (cbTuesday.Checked) days |= DaysOfWeek.Tuesday; if (cbWednesday.Checked) days |= DaysOfWeek.Wednesday;...
CoastN
25

Secundo la respuesta de Reed. Sin embargo, al crear la enumeración, debe especificar los valores para cada miembro de la enumeración para que haga una especie de campo de bits. Por ejemplo:

[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
fuente
1
Le ayudará si alguna vez necesita inspeccionar un valor de DaysOfWeek, por ejemplo, si está leyendo uno que está almacenado en una base de datos. Además, proporciona mejor documentación a otros programadores, ya que algunos enumeran los días de la semana a partir del lunes en lugar del domingo.
Jacob
3
Para mayor claridad y facilidad de mantenimiento, escribiría su enumeración 'Todos' como "Domingo | Lunes | Martes | Miércoles | Jueves | Viernes | Sábado" ... y usaría una notación similar para 'Días de semana' y 'Fin de semana'.
Robert Cartaino
Esto está bien si desea crear su propia enumeración, pero para responder a la pregunta, no puede alterar la enumeración DaysOfWeek existente
Robert Paulson
2
No "puede querer" ... DEBE. Las comparaciones bit a bit lo requieren. Dejar los valores sin especificar no funcionará.
Richard Anthony Hein
11

En mi situación particular, me gustaría usar System.DayOfWeek

No puede utilizar System.DayOfWeek como [Flags]enumeración porque no tiene control sobre él. Si desea tener un método que acepte múltiples DayOfWeek, deberá utilizar la paramspalabra clave

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

De lo contrario, puede crear su propia [Flags]enumeración como lo describen muchos otros respondedores y utilizar comparaciones bit a bit.

Robert Paulson
fuente
2
No sé por qué no pensé en params. Eso debería ser muy útil para usar enumeraciones que no son campos de bits.
Ronnie Overby
10
[Flags]
public enum DaysOfWeek
{
  Mon = 1,
  Tue = 2,
  Wed = 4,
  Thur = 8,
  Fri = 16,
  Sat = 32,
  Sun = 64
}

Debe especificar los números e incrementarlos así porque está almacenando los valores de forma bit a bit.

Entonces simplemente defina su método para tomar esta enumeración

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

y llamarlo haz algo como

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

Para verificar si se incluyó uno de los valores de enumeración, verifíquelos usando operaciones bit a bit como

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
  }
}
Tetraneutrón
fuente
No responde la pregunta. "En mi situación particular, me gustaría utilizar System.DayOfWeek"
Robert Paulson
Pero es exactamente lo que estaba buscando. Así que gracias de todos modos.
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
    }

llamar al método en este formato

MethodName (DaysOfWeek.Tuesday | DaysOfWeek.Thursday);

Implementar un método EnumToArray para que se pasen las opciones

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
fuente
No responde la pregunta. "En mi situación particular, me gustaría utilizar System.DayOfWeek"
Robert Paulson
Gracias, Robert, pero realmente no tienes que señalar esto en cada respuesta.
Ronnie Overby
@Ronnie - No es mi culpa que haya tantas respuestas casi duplicadas que en realidad no respondieron la pregunta.
Robert Paulson
@Rony: las enumeraciones ToString () y Enum.Parse () generarán / analizarán correctamente una enumeración [Flags] (siempre que tenga cuidado con las versiones de enumeración, por supuesto)
Robert Paulson
@Robert: no creo que nadie te esté culpando. Deja que los votos hablen.
Ronnie Overby
3

Marque su enumeración con el atributo [Flags]. También asegúrese de que todos sus valores sean mutuamente excluyentes (dos valores no pueden sumar para igualar otro) como 1,2,4,8,16,32,64 en su caso

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

Cuando tenga un método que acepte una enumeración DayOfWeek, use el operador or bit a bit (|) para usar varios miembros juntos. Por ejemplo:

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

Para verificar si el parámetro contiene un miembro específico, use el operador bit a bit y (&) con el miembro que está buscando.

if(arg & DayOfWeek.Sunday == DayOfWeek.Sunday)
Console.WriteLine("Contains Sunday");
statenjason
fuente
No responde la pregunta. "En mi situación particular, me gustaría utilizar System.DayOfWeek"
Robert Paulson
2

Reed Copsey tiene razón y lo agregaría a la publicación original si pudiera, pero no puedo, así que tendré que responder.

Es peligroso usar [Flags] en cualquier enumeración antigua. Creo que tienes que cambiar explícitamente los valores de enumeración a potencias de dos cuando usas banderas, para evitar choques en los valores. Consulte las directrices para FlagsAttribute y Enum .

Dan
fuente
-3

Algo de esta naturaleza debería mostrar lo que estás buscando:

[Flags]
public enum SomeName
{
    Name1,
    Name2
}

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