Страницы

Поиск по вопросам

вторник, 31 декабря 2019 г.

Флаги, или аналог множества в C#, как лучше реализовать?

#c_sharp #множества


Есть некий метод, пусть public static bool IsNewFileActual(sting oldFile, string
newFile, ComparsionFlags flags) сравнивающий 2 файла по набору критериев. Сами критерии,
для наглядности, например, такие: сравнение по дате последней записи в файл, версия
файла, MD5-хэш, размер файла.

В зависимости от некоторых условий, должна формироваться переменная flags, которая
будет задавать флаги для критериев проверки (например, флаг проверки по номеру версии
будет справедлив для *.EXE и *.DLL, а вот для *.PNG или *.HTML он не нужен).

В паскале (Delphi), я бы работал примерно так:

type 
   TComparsionFlags = set of (cfDate, cfVersion, cfHash, cfSize);
var
  CF : TComparsionFlags;
...
CF := [];
CF := CF + [cfVersion]; 
...
СF := CF + [cfHash, cfSize];
...
CF := CF - [cfDate];
...
if (cfDate in CF) then 
  begin
  ...
  end;


Иными словами, описал бы множество с возможными флагами, и использовал бы средства
языка для достижения своих целей.

Насколько я знаю, аналога паскалевских множеств в C# в чистом виде нет, и в качестве
замены можно использовать enum, что я и делаю:

[Flags]
public enum ComparsionFlags : byte
{
    cfVersion = 1,
    cfSize = 2,
    cfDate = 4,
    cfHash = 8,
}


После чего перед вызовом метода IsNewFileActual объявляю переменную ComparsionFlags
flags и далее задаю флаги так:

flags = ComparsionFlags.cfSize | ComparsionFlags.cfDate | ComparsionFlags.cfHash;


Как по мне, так это не очень удобно (по меньшей мере, как минимум, не лаконично),
как для задания самой переменной flags, так и для проверок значений, которые переданы
в этой переменной внутрь метода.

Собственно, вопрос и в заголовке, и вот, более детально:

Как наилучшим образом реализовать тип ComparsionFlags для использования переменных
данного типа в качестве набора флагов?

Может быть есть альтернативные, более удобные способы выполнить описанную задачу? 

Буду признателен за внимание и дельные советы/ответы. Спасибо.

P.S. Думал про List и HashTable, но есть ощущение, что это несколько не то...
Например, ничто не мешает добавить в List какой-то флаг n раз, и при удалении этого
флага из списка придется просматривать его весь, и удалять все вхождения этого флага
(или флагов), т.е. получаем, что нужно довольно много обвязки вокруг списка делать.
    


Ответы

Ответ 1



Использование флагов выглядит достаточно лаконично. Если переписать код с Delphi, получится примерно так: ComparsionFlags CF = default(ComparsionFlags); CF = CF | ComparsionFlags.cfVersion; ... СF = CF | ComparsionFlags.cfHash | ComparsionFlags.cfSize; ... CF = CF & ~ComparsionFlags.cfDate; ... if (CF.HasFlag(ComparsionFlags.cfDate)) { ... } Если использовать using static, то имя enum можно опускать: using static ComparsionFlags; ... ComparsionFlags CF = default(ComparsionFlags); CF |= cfVersion; ... СF |= cfHash | cfSize; ... CF &= ~cfDate; ... if (CF.HasFlag(cfDate)) { ... } В противовес, можно использовать класс HashSet, как указано в соседнем ответе: HashSet flags = new HashSet(); CF.Add(ComparsionFlags.cfVersion); ... СF.UnionWith(new[]{ComparsionFlags.cfHash,ComparsionFlags.cfSize}); ... CF.Remove(ComparsionFlags.cfDate); ... if (CF.Contains(ComparsionFlags.cfDate)) { ... } Как можно заметить, в этом случае не обязательно делать значения enum флагами, то есть значения могут идти и подряд: 1,2,3..., а не 1,2,4...

Ответ 2



Я бы написал HashSet

Комментариев нет:

Отправить комментарий