Qualche tempo fa ho scritto un set di metodi di estensione che funzionano per diversi tipi di Enum
s. Uno in particolare opere per quello che si sta cercando di realizzare e maniglie Enum
s con le FlagsAttribute
così come Enum
s con diversi tipi sottostanti.
public static tEnum SetFlags<tEnum>(this Enum e, tEnum flags, bool set, bool typeCheck = true) where tEnum : IComparable
{
if (typeCheck)
{
if (e.GetType() != flags.GetType())
throw new ArgumentException("Argument is not the same type as this instance.", "flags");
}
var flagsUnderlyingType = Enum.GetUnderlyingType(typeof(tEnum));
var firstNum = Convert.ToUInt32(e);
var secondNum = Convert.ToUInt32(flags);
if (set)
firstNum |= secondNum;
else
firstNum &= ~secondNum;
var newValue = (tEnum)Convert.ChangeType(firstNum, flagsUnderlyingType);
if (!typeCheck)
{
var values = Enum.GetValues(typeof(tEnum));
var lastValue = (tEnum)values.GetValue(values.Length - 1);
if (newValue.CompareTo(lastValue) > 0)
return lastValue;
}
return newValue;
}
Da lì puoi aggiungere altri metodi di estensione più specifici.
public static tEnum AddFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
SetFlags(e, flags, true);
}
public static tEnum RemoveFlags<tEnum>(this Enum e, tEnum flags) where tEnum : IComparable
{
SetFlags(e, flags, false);
}
Questo cambierà i tipi di messaggi Enum
come stai cercando di fare.
public static tEnum ChangeType<tEnum>(this Enum e) where tEnum : IComparable
{
return SetFlags(e, default(tEnum), true, false);
}
Tieni presente, tuttavia, che PUOI convertire tra qualsiasi Enum
e qualsiasi altro Enum
utilizzando questo metodo, anche quelli che non hanno flag. Per esempio:
public enum Turtle
{
None = 0,
Pink,
Green,
Blue,
Black,
Yellow
}
[Flags]
public enum WriteAccess : short
{
None = 0,
Read = 1,
Write = 2,
ReadWrite = 3
}
static void Main(string[] args)
{
WriteAccess access = WriteAccess.ReadWrite;
Turtle turtle = access.ChangeType<Turtle>();
}
La variabile turtle
avrà un valore di Turtle.Blue
.
Tuttavia, c'è sicurezza da Enum
valori indefiniti usando questo metodo. Per esempio:
static void Main(string[] args)
{
Turtle turtle = Turtle.Yellow;
WriteAccess access = turtle.ChangeType<WriteAccess>();
}
In questo caso, access
sarà impostato a WriteAccess.ReadWrite
, poiché WriteAccess
Enum
ha un valore massimo di 3.
Un altro effetto collaterale del mescolare Enum
i messaggi con FlagsAttribute
e quelli senza è che il processo di conversione non si tradurrà in una corrispondenza 1 a 1 tra i loro valori.
public enum Letters
{
None = 0,
A,
B,
C,
D,
E,
F,
G,
H
}
[Flags]
public enum Flavors
{
None = 0,
Cherry = 1,
Grape = 2,
Orange = 4,
Peach = 8
}
static void Main(string[] args)
{
Flavors flavors = Flavors.Peach;
Letters letters = flavors.ChangeType<Letters>();
}
In questo caso, letters
avrà un valore di Letters.H
invece di Letters.D
, poiché il valore di supporto di Flavors.Peach
è 8. Inoltre, una conversione da Flavors.Cherry | Flavors.Grape
a Letters
produrrebbe Letters.C
, il che può sembrare non intuitivo.