Как передать несколько значений перечисления в C #?

117

Иногда при чтении чужого кода C # я вижу метод, который принимает несколько значений перечисления в одном параметре. Я всегда думал, что это изящно, но никогда не разбирался в этом.

Что ж, теперь я думаю, что мне это может понадобиться, но не знаю, как

  1. настроить подпись метода, чтобы принять это
  2. работать со значениями в методе
  3. определить перечисление

для достижения такого рода вещей.


В моей конкретной ситуации я хотел бы использовать System.DayOfWeek, который определяется как:

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

Я хочу иметь возможность передавать одно или несколько значений DayOfWeek моему методу. Смогу ли я использовать это конкретное перечисление как есть? Как мне делать 3 перечисленных выше дела?

Ронни Оверби
источник
1
Как упоминалось ниже, вы можете использовать перечисление Flags. Вы можете ознакомиться с подробностями о перечислениях C # , в которых содержится дополнительная информация о работе с перечислениями Flags.
ChaseMedallion

Ответы:

181

Когда вы определяете перечисление, просто присвойте ему атрибут [Flags], установите значения в степени двойки, и это будет работать таким образом.

Больше ничего не меняется, кроме передачи в функцию нескольких значений.

Например:

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

Дополнительные сведения см. В документации MSDN по типам перечисления .


Редактировать в ответ на дополнения к вопросу.

Вы не сможете использовать это перечисление как есть, если только не захотите передать его как массив array / collection / params. Это позволит вам передать несколько значений. Синтаксис flags требует, чтобы Enum был указан как flags (или чтобы убрать язык так, как он не предназначен).

Рид Копси
источник
9
Я думаю, если вы хотите, чтобы это работало, значения должны быть определены как степени двойки, по одному биту для каждого значения. Что происходит, так это то, что вы передаете побитовое ИЛИ значений в параметре, которое вы можете проверить с помощью побитовых операций И. Если вы не зададите значения, например, 1,2,4,8 и т. Д., У вас возникнут проблемы.
Денис Троллер,
1
Простая установка только атрибута Flags не работает автоматически. Я только что попробовал.
Ричард Энтони Хейн,
1
Вы правы, и я удалил дезинформацию. Исправлен код Рида для него.
Мэтью Шарли,
@monoxide: Я исправил это в то же время - извините за отмену ваших правок.
Рид Копси,
2
Нет проблем ... Я все еще предпочитаю использовать шестнадцатеричный код для таких значений флагов, мне кажется более понятным, но каждый по своему.
Мэтью Шарли,
78

Я думаю, что более элегантным решением будет использование 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
источник
Если вы сделаете это таким образом, нужно знать еще одну важную вещь: он добавляет гораздо больше гибкости, когда вам нужно динамически заполнять перечисление. Например: если вы используете флажки для определения дней, в которые вы хотите, чтобы ваша программа запускалась, и вы попытаетесь выполнить это так, как это было сделано выше, код будет нечитаемым. Вместо этого вы можете сделать это следующим образом: ... DaysOfWeek days = new DaysOfWeek(); if (cbTuesday.Checked) days |= DaysOfWeek.Tuesday; if (cbWednesday.Checked) days |= DaysOfWeek.Wednesday;...
CoastN
25

Я второй ответ Рида. Однако при создании перечисления вы должны указать значения для каждого члена перечисления, чтобы получилось своего рода битовое поле. Например:

[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.
}
Иаков
источник
1
Это поможет вам, если вам когда-нибудь понадобится проверить значение DaysOfWeek, например, если вы читаете значение, хранящееся в базе данных. Кроме того, он обеспечивает лучшую документацию для других программистов, поскольку некоторые дни недели перечисляют дни недели, начиная с понедельника, а не с воскресенья.
Джейкоб
3
Для ясности и удобства обслуживания я бы написал перечисление All как «воскресенье | понедельник | вторник | среда | четверг | пятница | суббота» ... и использовал аналогичное обозначение для «будних дней» и «выходных».
Роберт Картаино
Это нормально, если вы хотите создать собственное перечисление, но чтобы ответить на вопрос, вы не можете изменить существующее перечисление DaysOfWeek
Роберт Полсон,
2
Не "может захотеть" ... ВЫ ДОЛЖНЫ. Побитовые сравнения требуют этого. Оставить значения неуказанными не получится.
Ричард Энтони Хейн,
11

В моей конкретной ситуации я хотел бы использовать System.DayOfWeek

Вы не можете использовать System.DayOfWeek в качестве [Flags]перечисления, потому что вы не можете его контролировать. Если вы хотите иметь метод, который принимает несколько, DayOfWeekвам нужно будет использовать paramsключевое слово

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

В противном случае вы можете создать собственное [Flags]перечисление, как указано многими другими респондентами, и использовать побитовые сравнения.

Роберт Полсон
источник
2
Не знаю, почему я не подумал о параметрах. Это должно быть очень полезно для использования перечислений, которые не являются битовыми полями.
Ронни Оверби,
10
[Flags]
public enum DaysOfWeek
{
  Mon = 1,
  Tue = 2,
  Wed = 4,
  Thur = 8,
  Fri = 16,
  Sat = 32,
  Sun = 64
}

Вы должны указать числа и увеличивать их таким образом, потому что он сохраняет значения поразрядным образом.

Затем просто определите свой метод, чтобы получить это перечисление

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

и чтобы назвать это, сделайте что-нибудь вроде

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

Чтобы проверить, было ли включено одно из значений перечисления, проверьте их с помощью побитовых операций, таких как

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
  }
}
тетранейтрона
источник
Не отвечает на вопрос. «В моей конкретной ситуации я бы хотел использовать System.DayOfWeek»
Роберт Полсон
Но это именно то, что я искал. Так что все равно спасибо.
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
    }

вызвать метод в этом формате

ИмяМетода (ДнейОфНеделя.Вторник | ДнейОфНеделя.Четверг);

Реализуйте метод EnumToArray, чтобы получить переданные параметры

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
источник
Не отвечает на вопрос. «В моей конкретной ситуации я бы хотел использовать System.DayOfWeek»
Роберт Полсон,
Спасибо, Роберт, но не обязательно указывать на это при каждом ответе.
Ronnie Overby
@Ronnie - Вряд ли я виноват в том, что существует так много почти повторяющихся ответов, которые на самом деле не дают ответа на вопрос.
Роберт Полсон,
@Rony - перечисления ToString () и Enum.Parse () будут правильно выводить / анализировать перечисление [Flags] (если, конечно, вы будете осторожны с версией перечисления)
Роберт Полсон
@ Роберт - я не думаю, что кто-то обвиняет тебя. Просто позвольте голосам говорить.
Ронни Оверби
3

Отметьте ваше перечисление атрибутом [Flags]. Также убедитесь, что все ваши значения являются взаимоисключающими (два значения не могут в сумме равняться другому), например 1,2,4,8,16,32,64 в вашем случае

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

Если у вас есть метод, который принимает перечисление DayOfWeek, используйте побитовый оператор или (|), чтобы использовать несколько членов вместе. Например:

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

Для того, чтобы проверить , если параметр содержит элемент конкретны, побитовое и оператор (&) с элементом , который вы проверяете для.

if(arg & DayOfWeek.Sunday == DayOfWeek.Sunday)
Console.WriteLine("Contains Sunday");
statenjason
источник
Не отвечает на вопрос. «В моей конкретной ситуации я бы хотел использовать System.DayOfWeek»
Роберт Полсон
2

Рид Копси прав, и я бы добавил к исходному сообщению, если бы мог, но я не могу, поэтому мне придется ответить вместо этого.

Опасно просто использовать [Flags] в любом старом перечислении. Я считаю, что вам нужно явно изменить значения перечисления на степени двойки при использовании флагов, чтобы избежать конфликтов в значениях. См. Рекомендации для FlagsAttribute и Enum .

Дэн
источник
-3

Что-то в этом роде должно показать то, что вы ищете:

[Flags]
public enum SomeName
{
    Name1,
    Name2
}

public class SomeClass()
{
    public void SomeMethod(SomeName enumInput)
    {
        ...
    }
}
Эндрю Симер
источник