Як ви передаєте кілька значень перерахунків у 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
Як було сказано нижче, ви можете використовувати переліки прапорів. Ви можете перевірити внески та переходи переліків на C # , що містить більше інформації про роботу з переліками прапорів.
ChaseMedallion

Відповіді:


181

Коли ви визначаєте enum, просто віднесіть його до [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 про типи перерахування .


Відредагуйте у відповідь на доповнення до запитання.

Ви не зможете використовувати цей перелік, як є, якщо ви не хочете зробити щось на зразок передавати його як масив / колекція / парами масив. Це дозволить вам передати кілька значень. Синтаксис прапорів вимагає, щоб Enum був вказаний як прапори (або для того, щоб бастардизувати мову таким чином, що його не розроблено).


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

Є ще одна важлива річ, яку потрібно знати, якщо ви робите це так: Це додає набагато більшої гнучкості, коли вам потрібно динамічно заповнювати свій перелік. Напр .: Якщо ви використовуєте прапорці для визначення днів, у які ви хочете запускати свою програму, і ви намагатиметесь виконати це так, як це зроблено вище, код буде нечитабельним. Тож замість цього ви можете це зробити так: ... DaysOfWeek days = new DaysOfWeek(); if (cbTuesday.Checked) days |= DaysOfWeek.Tuesday; if (cbWednesday.Checked) days |= DaysOfWeek.Wednesday;...
CoastN

25

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

[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
Для наочності та ремонтопридатності я б написав ваші перерахунки "Усі" як "Неділя | Понеділок | Вівторок | Середа | Четвер | П'ятниця | Субота" ... і використовував подібні позначення для "Будні" та "Вихідні дні".
Роберт Картенто

Це добре, якщо ви хочете створити свій власний перелік, але, щоб відповісти на питання, ви не можете змінити існуючий перелік «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"
Роберт Полсон

Але це саме те, що я шукав. Тож дякую все одно.
Тілліто

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
    }

викликати метод у такому форматі

MethodName (DaysOfWeek.Tuesday | DaysOfWeek.Th Thursday);

Реалізуйте метод 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();
        }

Не відповідає на запитання. "У моїй конкретній ситуації я хотів би скористатися System.DayOfWeek"
Роберт Полсон

Дякую, Роберто, але тобі не потрібно вказувати це на кожній відповіді.
Ронні Овербі

@Ronnie - Навряд чи я винен, що існує стільки відповідей, що майже повторюються, що насправді не відповіли на питання.
Роберт Поулсон

@Rony - перерахунки ToString () та Enum.Parse () будуть правильно виводити / аналізувати перерахування [Прапорів] (доки ви обережно ставитеся до версії перерахунку)
Роберт Полсон,

@Robert - Я не думаю, що ніхто тебе звинувачує. Просто нехай голоси ведуть розмови.
Ронні Овербі

3

Позначте перерахунок за допомогою атрибута [Прапори]. Також переконайтесь, що всі ваші значення взаємовиключні (два значення не можуть дорівнювати іншим), як у вашому випадку 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");

Не відповідає на запитання. "У моїй конкретній ситуації я хотів би використовувати System.DayOfWeek"
Роберт Полсон

2

Рід Копсі є правильним, і я би додавав його до оригінальної публікації, якщо зможу, але не можу, тому мені доведеться відповісти замість цього.

Небезпечно просто використовувати [Прапори] на будь-якому старому переліку. Я вважаю, що вам потрібно чітко змінити значення перерахунків на потужність двох при використанні прапорів, щоб уникнути зіткнення значень. Дивіться вказівки щодо FlagsAttribute та Enum .



-3

Щось такого характеру повинно показувати, що ви шукаєте:

[Flags]
public enum SomeName
{
    Name1,
    Name2
}

public class SomeClass()
{
    public void SomeMethod(SomeName enumInput)
    {
        ...
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.