I have to build an extension method for each flag type I declare, like so:
public static EventMessageScope SetFlag(this EventMessageScope flags,
EventMe
Here is another quick and dirty way to SetFlag for any Enum:
public static T SetFlag<T>(this T flags, T flag, bool value) where T : struct, IComparable, IFormattable, IConvertible
{
int flagsInt = flags.ToInt32(NumberFormatInfo.CurrentInfo);
int flagInt = flag.ToInt32(NumberFormatInfo.CurrentInfo);
if (value)
{
flagsInt |= flagInt;
}
else
{
flagsInt &= ~flagInt;
}
return (T)(Object)flagsInt;
}
public static class SomeEnumHelperMethodsThatMakeDoingWhatYouWantEasier
{
public static T IncludeAll<T>(this Enum value)
{
Type type = value.GetType();
object result = value;
string[] names = Enum.GetNames(type);
foreach (var name in names)
{
((Enum) result).Include(Enum.Parse(type, name));
}
return (T) result;
//Enum.Parse(type, result.ToString());
}
/// <summary>
/// Includes an enumerated type and returns the new value
/// </summary>
public static T Include<T>(this Enum value, T append)
{
Type type = value.GetType();
//determine the values
object result = value;
var parsed = new _Value(append, type);
if (parsed.Signed is long)
{
result = Convert.ToInt64(value) | (long) parsed.Signed;
}
else if (parsed.Unsigned is ulong)
{
result = Convert.ToUInt64(value) | (ulong) parsed.Unsigned;
}
//return the final value
return (T) Enum.Parse(type, result.ToString());
}
/// <summary>
/// Check to see if a flags enumeration has a specific flag set.
/// </summary>
/// <param name="variable">Flags enumeration to check</param>
/// <param name="value">Flag to check for</param>
/// <returns></returns>
public static bool HasFlag(this Enum variable, Enum value)
{
if (variable == null)
return false;
if (value == null)
throw new ArgumentNullException("value");
// Not as good as the .NET 4 version of this function,
// but should be good enough
if (!Enum.IsDefined(variable.GetType(), value))
{
throw new ArgumentException(string.Format(
"Enumeration type mismatch. The flag is of type '{0}', " +
"was expecting '{1}'.", value.GetType(),
variable.GetType()));
}
ulong num = Convert.ToUInt64(value);
return ((Convert.ToUInt64(variable) & num) == num);
}
/// <summary>
/// Removes an enumerated type and returns the new value
/// </summary>
public static T Remove<T>(this Enum value, T remove)
{
Type type = value.GetType();
//determine the values
object result = value;
var parsed = new _Value(remove, type);
if (parsed.Signed is long)
{
result = Convert.ToInt64(value) & ~(long) parsed.Signed;
}
else if (parsed.Unsigned is ulong)
{
result = Convert.ToUInt64(value) & ~(ulong) parsed.Unsigned;
}
//return the final value
return (T) Enum.Parse(type, result.ToString());
}
//class to simplfy narrowing values between
//a ulong and long since either value should
//cover any lesser value
private class _Value
{
//cached comparisons for tye to use
private static readonly Type _UInt32 = typeof (long);
private static readonly Type _UInt64 = typeof (ulong);
public readonly long? Signed;
public readonly ulong? Unsigned;
public _Value(object value, Type type)
{
//make sure it is even an enum to work with
if (!type.IsEnum)
{
throw new ArgumentException(
"Value provided is not an enumerated type!");
}
//then check for the enumerated value
Type compare = Enum.GetUnderlyingType(type);
//if this is an unsigned long then the only
//value that can hold it would be a ulong
if (compare.Equals(_UInt32) || compare.Equals(_UInt64))
{
Unsigned = Convert.ToUInt64(value);
}
//otherwise, a long should cover anything else
else
{
Signed = Convert.ToInt64(value);
}
}
}
}
The reason I'm finding is that since enum is a value type, you cannot pass it in and set its type. To all of you that think its stupid, I say this to you: Not all developers understand bit flags and how to turn them on or off (which is much less intuitive).
Not a stupid idea, just not possible.
I've done something that works for me and that's very simple...
public static T SetFlag<T>(this Enum value, T flag, bool set)
{
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
// note: AsInt mean: math integer vs enum (not the c# int type)
dynamic valueAsInt = Convert.ChangeType(value, underlyingType);
dynamic flagAsInt = Convert.ChangeType(flag, underlyingType);
if (set)
{
valueAsInt |= flagAsInt;
}
else
{
valueAsInt &= ~flagAsInt;
}
return (T)valueAsInt;
}
Usage:
var fa = FileAttributes.Normal;
fa = fa.SetFlag(FileAttributes.Hidden, true);
The &
operator will give you the same answer with a & b
as it will with b & a
, so
(EventMessaageScope.Private).Get(EventMessageScope.Private | EventMessageScope.PublicOnly)
is the same as writing
(EventMessageScope.Private | EventMessageScope.PublicOnly).Get(EventMessaageScope.Private)
If you just want to know if the value is the same as EventMessaageScope.Public, then just use equals:
EventMessageScope.Private == EventMessageScope.Public
Your method will always return false
for (EventMessageScope.None).Get(EventMessaageScope.None)
because None == 0
and it only returns true when the result of the AND operation is not zero. 0 & 0 == 0
.
To answer part of your your question: the Get function works properly according to binary logic - it checks for any match. If you want to match the whole set of flags, consider this instead:
return ((flags & flag) != flag);
Regarding "why isn't there SetFlag"... probably because it's not really needed. Flags are integers. There is already a convention for dealing with those and it applies to flags as well. If you don't want to write it with |
and &
- that's what the custom static addons are for - you can just use your own functions as you demonstrated yourself :)