Поиск наивысшего установленного флага в значении перечисления

#c# #.net #enums

#c# #.net #перечисления

Вопрос:

Я использую атрибут enum with flags как способ отслеживания статуса.

Примером является следующее:

 Created = 1
Completed = 2
Dispatched = 4
  

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

 Item.Status = Status.Created | Status.Completed
  

мифический метод вернул бы 2 — как завершенный, это флаг, установленный с наибольшим значением.

 GetMaxSetFlagValue(Item.Status) // returns 2
  

Я нашел вопросы, которые вращались вокруг фактического перечисления, а не значения, которое использует флаги. Я уверен, что этого можно достичь с помощью Linq …?

Ответ №1:

Должно сработать что-то вроде следующего:

 static int GetMaxSetFlagValue<T>(T flags) where T : struct
{
   int value = (int)Convert.ChangeType(flags, typeof(int));
   IEnumerable<int> setValues = Enum.GetValues(flags.GetType()).Cast<int>().Where(f => (f amp; value) == f);
   return setValues.Any() ? setValues.Max() : 0;
}
  

Метод завершится ошибкой, если T не является типом перечисления, поэтому проверку предпочтительно выполнять в начале метода. Также это не будет работать для перечисления с базовым типом, большим, чем int (т. Е. long ).

Ответ №2:

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

 var maxStatus = Item.Status.GetFlags().Max();
  

Вывод: maxStatus = Завершено

 public static class EnumExtensions {

    /// <summary>Enumerates get flags in this collection.</summary>
    ///
    /// <param name="value">The value.
    /// </param>
    ///
    /// <returns>An enumerator that allows foreach to be used to process get flags in this collection.</returns>
    public static IEnumerable<T> GetFlags<T> (this T value) where T : struct {
        return GetFlags (value, Enum.GetValues (value.GetType ()).Cast<T> ().ToArray ());
    }

    /// <summary>Enumerates get flags in this collection.</summary>
    ///
    /// <param name="value"> The value.
    /// </param>
    /// <param name="values">The values.
    /// </param>
    ///
    /// <returns>An enumerator that allows foreach to be used to process get flags in this collection.</returns>
    private static IEnumerable<T> GetFlags<T> (T value, T [] values) where T : struct {
        if (!typeof (T).IsEnum) {
            throw new ArgumentException ("Type must be an enum.");
        }
        ulong bits = Convert.ToUInt64 (value);
        var results = new List<T> ();
        for (int i = values.Length - 1; i >= 0; i--) {
            ulong mask = Convert.ToUInt64 (values [i]);
            if (i == 0 amp;amp; mask == 0L)
                break;
            if ((bits amp; mask) == mask) {
                results.Add (values [i]);
                bits -= mask;
            }
        }
        if (bits != 0L)
            return Enumerable.Empty<T> ();
        if (Convert.ToUInt64 (value) != 0L)
            return results.Reverse<T> ();
        if (bits == Convert.ToUInt64 (value) amp;amp; values.Length > 0 amp;amp; Convert.ToUInt64 (values [0]) == 0L)
            return values.Take (1);
        return Enumerable.Empty<T> ();
    }
}
  

Ответ №3:

Поскольку вы можете выполнять обратное преобразование в uint, вы могли бы использовать:

 public uint LowestBit(uint x) 
{
    return ~(xamp;x-1)amp;x;
}
public uint HighestBit(uint x)
{
    uint last = x;
    while (x!=0) 
    {
        last=x;
        xamp;=x-1;
    }
    return last;
}