Определение того, находится ли значение перечисления в списке (C#)

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

Я хотел бы проверить, идет ли дождь или гроза.

public enum WeatherType : byte
{ Sunny = 0, Cloudy = 1, Thunderstorm = 2, Raining = 4, Snowing = 8, MostlyCloudy = 16 }

Я думал, что могу сделать что-то вроде:

WeatherType _badWeatherTypes = WeatherType.Thunderstorm | WeatherType.Raining;
if(currentWeather.Type == _badWeatherTypes)
{
 return false;//don't bike
}

но это не работает, потому что _badWeatherTypes представляет собой комбинацию обоих типов. Я бы хотел, чтобы они были разделены, потому что это должно быть учебным опытом, и его разделение может быть полезно в других ситуациях (IE, неоплаченный счет-фактура и т. Д.).

Я бы также предпочел не делать: (это убрало бы возможность настройки для нескольких человек)

if(WeatherType.Thunderstorm)
{
 return false; //don't bike
}
etc...

person Nathan Koop    schedule 02.10.2008    source источник


Ответы (6)


Ваш текущий код скажет, точно ли это "дождь и гроза". Чтобы узнать, идет ли «дождь, гроза и, возможно, что-то еще», вам нужно:

if ((currentWeather.Type & _badWeatherTypes) == _badWeatherTypes)

Чтобы узнать, идет ли "дождь или гроза и, возможно, что-то еще", вам нужно:

if ((currentWeather.Type & _badWeatherTypes) != 0)

РЕДАКТИРОВАТЬ (для полноты):

Было бы хорошо использовать FlagsAttribute, т.е. украсить тип [Flags]. Это не обязательно ради этой побитовой логики, но влияет на то, как ведет себя ToString(). Компилятор C# игнорирует этот атрибут (по крайней мере, на данный момент; в спецификации C# 3.0 он не упоминается), но обычно это хорошая идея для перечислений, которые фактически являются флагами, и документирует предполагаемое использование типа. В то же время соглашение заключается в том, что когда вы используете флаги, вы ставите имя перечисления во множественное число, поэтому вы должны изменить его на WeatherTypes (поскольку любое фактическое значение фактически равно 0 или более типам погоды).

Также стоит подумать о том, что на самом деле означает «Солнечный». В настоящее время он имеет значение 0, что означает отсутствие всего остального; не может быть одновременно и солнечно, и идет дождь (что, конечно, физически возможно). Пожалуйста, не пишите код, запрещающий радугу! ;) С другой стороны, если в вашем реальном случае использования вы действительно хотите значение, которое означает «отсутствие всех других значений», тогда все в порядке.

person Jon Skeet    schedule 02.10.2008
comment
[Флаги] действительно подходят. - person Mark Bessey; 03.10.2008
comment
Что ж, это определенно хорошо, но это совершенно не соответствует реальному вопросу. Это не поможет и не помешает побитовой логике. - person Jon Skeet; 03.10.2008
comment
Вы были правы насчет Санни, мне нужно было это изменить. Спасибо всем за помощь. - person Nathan Koop; 03.10.2008

Я не уверен, что это должен быть флаг - я думаю, что у вас должен быть ввод диапазона для:

  • Температура
  • сколько идет дождь
  • Сила ветра
  • любые другие входные данные, которые вам нравятся (например, гроза)

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

Я думаю, вы также должны внести свой вклад в то, насколько вероятно, что погода останется прежней для езды на велосипеде домой. Критерии могут быть разными - вы сможете легче принять душ и переодеться, когда вернетесь домой.

Если вы действительно хотите сделать это интересным, собирайте входные данные из API службы погоды и каждый день оценивайте решение — да, я должен был зациклиться, или нет, это была ошибка. Тогда, возможно, вы сможете научить приложение принимать более правильные решения.

Следующий шаг — «обнародовать» свое решение и посмотреть, слышат ли другие люди, что вы принимаете такие же решения.

person LawrenceF    schedule 09.11.2011
comment
Ой, я всегда пропускаю этот шаг! - person LawrenceF; 13.06.2017

используйте атрибут FlagsAttribute. Это позволит вам использовать перечисление в качестве битовой маски.

person MagicKat    schedule 02.10.2008
comment
Вы можете использовать перечисление как битовую маску без флагов; Я полагаю, что FlagsAttribute просто меняет то, как работает ToString. Конечно, было бы неплохо включить его. - person Jon Skeet; 03.10.2008
comment
Он просто меняет поведение ToString, но нет гарантии, что будущие версии .NET не изменят другое поведение (например, компилятор выдает ошибки, если значения не являются степенями двойки), поэтому вы всегда должны использовать его при использовании перечисление флагов. - person Scott Dorman; 03.10.2008
comment
Ах, я не знал, что он просто изменил ToString(). Интересный. - person MagicKat; 03.10.2008
comment
Если вы не укажете значения, Flag установит для них соответствующие степени двойки (по крайней мере, в VB). - person Mark Brackett; 03.10.2008
comment
К сожалению, флаги не изменяют автоматически сгенерированные значения в C#. Что касается отсутствия гарантии того, что будущие версии .NET не изменят другое поведение - это все равно, что сказать, что нет гарантии, что сборщик мусора по-прежнему будет работать в будущих версиях, лучше установить для переменных значение null, чтобы помочь ему. - person Jon Skeet; 03.10.2008
comment
Чтобы сказать больше о моем последнем комментарии - это потребовало бы от команды С# нарушения раздела 7.3.2 спецификации без уважительной причины. Да, использование флагов — хорошая идея. Нет, это действительно, действительно не нужно. - person Jon Skeet; 03.10.2008
comment
Я считаю, что FlagsAttribute также меняет работу enum.Parse(). - person James Curran; 03.10.2008
comment
Хммм... похоже, что мой последний комментарий не был сделан... Джон прав, использование флагов не приводит к тому, что компилятор автоматически генерирует правильные значения степени 2, даже в VB. (Я только что проверил это.) - person Scott Dorman; 03.10.2008
comment
Джеймс, ты прав, извини, я забыл об этом. Хороший улов. - person Jon Skeet; 03.10.2008
comment
@Jon Skeet, мой комментарий об отсутствии гарантий поведения на самом деле был аргументом в пользу использования атрибута Flags. Тот факт, что вы можете использовать перечисление, как если бы это было перечисление флагов без атрибута, не означает, что вы должны это делать. Нет никакой гарантии, что эти сценарии всегда будут работать в будущем. - person Scott Dorman; 03.10.2008
comment
Используя атрибут Flags, вы явно указываете компилятору и среде выполнения, что вы ожидаете определенного поведения от перечисления. - person Scott Dorman; 03.10.2008
comment
Скотт: Да, я знаю, что ты настаивал на этом. И я согласен, что это хорошая идея. Я просто не верю, что вам это действительно нужно, и аргумент спецификации может измениться ошибочно, поскольку вы можете применить его к чему-либо еще. Они могут переопределить сложение и вычитание — как от этого защититься? - person Jon Skeet; 03.10.2008
comment
Это действительно не нужно для побитовой стороны - для ToString и Parse необходимо получить правильное поведение, и это также хорошо с точки зрения документирования ожиданий. - person Jon Skeet; 03.10.2008
comment
Только что просмотрел спецификацию и вообще не вижу ссылок на атрибут Flags, поэтому я не верю, что это влияет на компилятор - только на структуру. - person Jon Skeet; 03.10.2008
comment
Глядя на код Enum.Parse, не похоже, что он учитывает атрибут Flags. Если он получает список, разделенный запятыми, он будет побитовым или вместе, несмотря ни на что. - person Scott Dorman; 03.10.2008
comment
Правильно, сейчас это просто влияет на время выполнения. (Однако было бы неплохо, если бы компилятор проверял значения перечисления.) В этом смысле вы также правы в том, что вам это на самом деле не нужно, но оно явно сообщает всем, кто смотрит, как должно вести себя перечисление. Нет причин не использовать его. - person Scott Dorman; 03.10.2008
comment
Он был добавлен в среду выполнения не просто так, а именно для того, чтобы указать, что перечисление можно рассматривать как битовое поле. Есть определенные вещи, которые вы не можете защитить от переопределения (как в ваших примерах), но есть вещи, которые вы можете сделать, чтобы смягчить результаты. - person Scott Dorman; 03.10.2008
comment
Да, это было добавлено по какой-то причине, но вы действительно собираетесь предположить, что существует более миллиона шансов на один, что команда C # нарушит спецификацию (которая очень точно определяет поведение) без причины и потенциально сломает много приложений? - person Jon Skeet; 03.10.2008
comment
И да, я согласен, что нет причин не использовать его. Я только возражал против того, что это может сломать логику в будущем :) Спасибо за подсказку о Parse, кстати. Исправлено мое расширенное объяснение. - person Jon Skeet; 03.10.2008
comment
Нет, я вовсе этого не предлагаю (хотя это произошло при переходе с 1.1 -> 2.0). В любом случае, более вероятный сценарий состоит в том, что компилятор получает некоторые знания о них и может выполнять больше проверок во время компиляции. - person Scott Dorman; 03.10.2008
comment
Вау - я был совершенно неправ. Кажется, что Flags не автоматически генерирует правильные значения для вас ни в VB, ни в C#, и не будет жаловаться, если вы затем побитово их. Я не уверен, почему я думал, что это так. Извините, пока я бегу искать любые ошибки перечисления за последние 5 лет! - person Mark Brackett; 03.10.2008


Вы должны использовать атрибут Flags в своем перечислении. Кроме того, вам также необходимо проверить, установлен ли конкретный флаг:

(currentWeather.Type & WeatherType.Thunderstorm == WeatherType.Thunderstorm)

Это проверит, установлен ли для currentWeather.Type флаг WeatherType.Thunderstorm.

person Scott Dorman    schedule 02.10.2008

Я бы не стал ограничивать себя битовым миром. Перечисления и побитовые операторы, как вы выяснили, не одно и то же. Если вы хотите решить эту проблему с помощью побитовых операторов, я бы придерживался только их, т.е. не заморачивался с перечислениями. Однако я хотел бы что-то вроде следующего:

        WeatherType[] badWeatherTypes = new WeatherType[]
        {   
            WeatherType.Thunderstorm, 
            WeatherType.Raining
        };

        if (Array.IndexOf(badWeatherTypes, currentWeather.Type) >= 0)
        {
                        return false;
        }
person Ken Wootton    schedule 02.10.2008
comment
Это именно то, для чего предназначены перечисления флагов (битовые поля). Со страницы MSDN в FlagsAttribute: битовые поля обычно используются для списков элементов, которые могут встречаться в комбинации... Поэтому битовые поля предназначены для объединения с побитовой операцией ИЛИ для создания безымянных значений. - person Scott Dorman; 03.10.2008