Como você passa vários valores enum em C #?

117

Às vezes, ao ler o código C # de outras pessoas, vejo um método que aceitará vários valores enum em um único parâmetro. Sempre achei que era legal, mas nunca olhei para isso.

Bem, agora eu acho que posso ter uma necessidade, mas não sei como

  1. configurar a assinatura do método para aceitar isso
  2. trabalhar com os valores do método
  3. definir o enum

para conseguir esse tipo de coisa.


Em minha situação específica, gostaria de usar System.DayOfWeek, que é definido como:

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

Eu quero passar um ou mais dos valores DayOfWeek para meu método. Serei capaz de usar este enum específico como está? Como faço as 3 coisas listadas acima?

Ronnie Overby
fonte
1
Conforme mencionado abaixo, você pode usar uma enumeração Flags. Você pode querer verificar Os prós e contras das enums C # , que contém mais informações sobre como trabalhar com enums de sinalizadores.
ChaseMedallion de

Respostas:

181

Ao definir o enum, apenas atribua-o com [Flags], defina os valores para potências de dois e funcionará desta forma.

Nada mais muda, a não ser passar vários valores para uma função.

Por exemplo:

[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 obter mais detalhes, consulte a documentação do MSDN sobre Tipos de Enumeração .


Edite em resposta aos acréscimos à pergunta.

Você não poderá usar aquele enum como está, a menos que queira fazer algo como passá-lo como um array / coleção / array de parâmetros. Isso permitiria passar vários valores. A sintaxe dos sinalizadores requer que o Enum seja especificado como sinalizadores (ou para bastardizar a linguagem de uma forma que não foi projetada).

Reed Copsey
fonte
9
Acho que se você quiser que isso funcione, os valores precisam ser definidos como potências de dois, um único bit para cada valor. O que acontece é que você passa o OR bit a bit dos valores no parâmetro, que você pode verificar usando operações AND bit a bit. Se você não se preocupar em definir os valores como, por exemplo, 1,2,4,8 etc, você terá problemas.
Denis Troller
1
Apenas definir o atributo Flags sozinho não funciona automaticamente. Eu apenas tentei.
Richard Anthony Hein
1
Você está certo e eu removi a desinformação. Corrigido o código de Reed também para ele.
Matthew Scharley
@monoxide: Consertei ao mesmo tempo - desculpe por substituir suas edições.
Reed Copsey
2
Não tem problema ... Eu ainda prefiro usar hex para valores de flag assim, parece mais claro para mim, mas cada um no seu.
Matthew Scharley
78

Acho que a solução mais elegante é 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
fonte
Há outra coisa importante a saber, se você fizer isso dessa maneira: isso adiciona muito mais flexibilidade quando você precisa preencher sua enumeração dinamicamente. Ex: Se você usar caixas de seleção para definir os dias em que deseja que seu programa seja executado e tentar fazer isso da forma que foi feita acima, o código ficará ilegível. Então, em vez disso, você pode fazer desta maneira: ... DaysOfWeek days = new DaysOfWeek(); if (cbTuesday.Checked) days |= DaysOfWeek.Tuesday; if (cbWednesday.Checked) days |= DaysOfWeek.Wednesday;...
CoastN
25

Eu apoio a resposta de Reed. No entanto, ao criar o enum, você deve especificar os valores para cada membro do enum para que ele crie uma espécie de campo de bits. Por exemplo:

[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
fonte
1
Isso o ajudará se você precisar inspecionar um valor de DaysOfWeek, por exemplo, se estiver lendo um que está armazenado em um banco de dados. Além disso, fornece uma documentação melhor para outros programadores, uma vez que alguns dias da semana enumerados começando na segunda-feira em vez de domingo.
Jacob
3
Para maior clareza e facilidade de manutenção, eu escreveria seu enum 'Todos' como "Domingo | Segunda | Terça | Quarta | Quinta | Sexta | Sábado" ... e usaria uma notação semelhante para 'Dias da semana' e 'Fim de semana'.
Robert Cartaino
Isso é bom se você deseja criar seu próprio enum, mas para responder à pergunta você não pode alterar o enum DaysOfWeek existente
Robert Paulson
2
Não "pode ​​querer" ... você DEVE. As comparações bit a bit exigem isso. Deixar os valores não especificados não funcionará.
Richard Anthony Hein
11

Em minha situação particular, gostaria de usar o System.DayOfWeek

Você não pode usar o System.DayOfWeek como uma [Flags]enumeração porque você não tem controle sobre ele. Se você deseja ter um método que aceita múltiplos, DayOfWeekentão você terá que usar a paramspalavra - chave

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

Caso contrário, você pode criar sua própria [Flags]enumeração conforme descrito por vários outros respondentes e usar comparações bit a bit.

Robert Paulson
fonte
2
Não sei porque não pensei em params. Isso deve ser muito útil para usar enums que não são campos de bits.
Ronnie Overby
10
[Flags]
public enum DaysOfWeek
{
  Mon = 1,
  Tue = 2,
  Wed = 4,
  Thur = 8,
  Fri = 16,
  Sat = 32,
  Sun = 64
}

Você tem que especificar os números e incrementá-los assim, porque isso está armazenando os valores de forma bit a bit.

Em seguida, apenas defina seu método para obter este enum

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

e chamá-lo de fazer algo como

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

Para verificar se um dos valores enum foi incluído, verifique-os usando operações 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
  }
}
Tetraneutron
fonte
Não responde à pergunta. "Na minha situação particular, gostaria de usar o System.DayOfWeek"
Robert Paulson
Mas é exatamente o que estou procurando. Então, obrigado mesmo assim.
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
    }

chame o método neste formato

MethodName (DaysOfWeek.T Terça | DaysOfWeek.Thucted);

Implementar um método EnumToArray para passar as opçõ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
fonte
Não responde à pergunta. "Na minha situação particular, gostaria de usar o System.DayOfWeek"
Robert Paulson
Obrigado, Robert, mas você realmente não precisa apontar isso em todas as respostas.
Ronnie Overby
@Ronnie - Não é minha culpa que haja tantas respostas quase duplicadas que não respondem de fato à pergunta.
Robert Paulson
@Rony - os enums ToString () e Enum.Parse () irão gerar / analisar corretamente uma enumeração [Flags] (contanto que você tome cuidado com a versão da enumeração, é claro)
Robert Paulson
@Robert - Acho que ninguém está culpando você. Apenas deixe os votos falarem.
Ronnie Overby
3

Marque seu enum com o atributo [Sinalizadores]. Além disso, certifique-se de que todos os seus valores sejam mutuamente exclusivos (dois valores não podem somar igual a outro) como 1,2,4,8,16,32,64 no seu caso

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

Quando você tem um método que aceita um enum DayOfWeek, use o bit a bit ou o operador (|) para usar vários membros juntos. Por exemplo:

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

Para verificar se o parâmetro contém um membro específico, use o bit a bit e o operador (&) com o membro que você está verificando.

if(arg & DayOfWeek.Sunday == DayOfWeek.Sunday)
Console.WriteLine("Contains Sunday");
statenjason
fonte
Não responde à pergunta. "Na minha situação particular, gostaria de usar o System.DayOfWeek"
Robert Paulson
2

Reed Copsey está correto e eu acrescentaria à postagem original se pudesse, mas não posso, então terei de responder.

É perigoso usar apenas [Sinalizadores] em qualquer enum antigo. Acredito que você tenha que alterar explicitamente os valores de enum para potências de dois ao usar sinalizadores, para evitar conflitos nos valores. Consulte as diretrizes para FlagsAttribute e Enum .

Dan
fonte
-3

Algo dessa natureza deve mostrar o que você está procurando:

[Flags]
public enum SomeName
{
    Name1,
    Name2
}

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