Skip to content

Instantly share code, notes, and snippets.

@taross-f
Last active August 19, 2016 10:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save taross-f/13774ef5355fae844b6cefec083de8ef to your computer and use it in GitHub Desktop.
Save taross-f/13774ef5355fae844b6cefec083de8ef to your computer and use it in GitHub Desktop.
Enum with FlagAttribute extension
void Main()
{
var f = BitHoge.a;
f = f.WithFlag(BitHoge.b);
f.Dump(); // a, b
f.HasFlag(BitHoge.a).Dump(); // true
f.HasFlag(BitHoge.c).Dump(); // false
f = f.WithoutFlag(BitHoge.a);
f.Dump(); // b
f.HasFlag(BitHoge.a).Dump(); // false
f = f.WithFlag(BitHoge.c).WithFlag(BitHoge.d);
f.Dump();
f = f.WithoutFlag(BitHoge.b).WithoutFlag(BitHoge.c);
f.Dump();
f = f.WithFlag(BitHoge.a).WithFlag(BitHoge.c);
f.Dump();
f = f.WithoutFlag(BitHoge.b);
f.Dump();
f = f.WithFlag((BitHoge)16);
f.Dump();
Test();
}
void Test()
{
var hoge = BitHoge.a;
var range = Enumerable.Range(0, 1000000)
.ToList();
var sw = Stopwatch.StartNew();
range.ForEach(x =>
{
var b = hoge.WithFlag(BitHoge.c);
});
sw.Stop();
sw.ElapsedMilliseconds.Dump();
sw.Restart();
range.ForEach(x =>
{
var b = hoge | BitHoge.c;
});
sw.Stop();
sw.ElapsedMilliseconds.Dump();
sw.Restart();
range.ForEach(x =>
{
var b = Convert.ToInt64(hoge);
});
sw.Stop();
sw.ElapsedMilliseconds.Dump();
sw.Restart();
range.ForEach(x =>
{
var b = (BitHoge)Enum.ToObject(typeof(BitHoge), 1);
});
sw.Stop();
sw.ElapsedMilliseconds.Dump();
}
public static class FlagEnumEx
{
static Dictionary<Type, Type> EnumToUnderlyingType = new Dictionary<System.Type, System.Type>();
public static T WithFlag<T>(this T origin, T flag)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("T is not enum.");
// こいつくそ重いのとEnumチェックで動きはするので除く
// if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
// throw new ArgumentException("T does not have FlagsAttribute.");
var type = Enum.GetUnderlyingType(typeof(T));
// var type = EnumToUnderlyingTypeWithCache(typeof(T)); // キャッシュしたら変わるかなと思ったらそんな速度変わらず
switch (type.Name)
{
case "Byte" : return (T)Enum.ToObject(origin.GetType(), Convert.ToByte(origin) | Convert.ToByte(flag));
case "Int16" : return (T)Enum.ToObject(origin.GetType(), Convert.ToInt16(origin) | Convert.ToInt16(flag));
case "Int32" : return (T)Enum.ToObject(origin.GetType(), Convert.ToInt32(origin) | Convert.ToInt32(flag));
case "Int64" : return (T)Enum.ToObject(origin.GetType(), Convert.ToInt64(origin) | Convert.ToInt64(flag));
case "SByte" : return (T)Enum.ToObject(origin.GetType(), Convert.ToSByte(origin) | Convert.ToSByte(flag));
case "UInt16" : return (T)Enum.ToObject(origin.GetType(), Convert.ToUInt16(origin) | Convert.ToUInt16(flag));
case "UInt32" : return (T)Enum.ToObject(origin.GetType(), Convert.ToUInt32(origin) | Convert.ToUInt32(flag));
case "UInt64" : return (T)Enum.ToObject(origin.GetType(), Convert.ToUInt64(origin) | Convert.ToUInt64(flag));
}
throw new ArgumentException("T is invalid."); // ここたどり着かない
// .NET 4 以上だと手抜きだけどこれが楽だし処理も早い(どちらにせよ静的にチェックできないので…)
// dynamic dynamicOrigin = origin;
// dynamic dynamicFlag = flag;
//
// return dynamicOrigin | dynamicFlag;
}
public static T WithoutFlag<T>(this T origin, T flag)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("T is not enum.");
var type = Enum.GetUnderlyingType(typeof(T));
switch (type.Name)
{
case "Byte": return (T)Enum.ToObject(origin.GetType(), Convert.ToByte(origin) & ~Convert.ToByte(flag));
case "Int16": return (T)Enum.ToObject(origin.GetType(), Convert.ToInt16(origin) & ~Convert.ToInt16(flag));
case "Int32": return (T)Enum.ToObject(origin.GetType(), Convert.ToInt32(origin) & ~Convert.ToInt32(flag));
case "Int64": return (T)Enum.ToObject(origin.GetType(), Convert.ToInt64(origin) & ~Convert.ToInt64(flag));
case "SByte": return (T)Enum.ToObject(origin.GetType(), Convert.ToSByte(origin) & ~Convert.ToSByte(flag));
case "UInt16": return (T)Enum.ToObject(origin.GetType(), Convert.ToUInt16(origin) & ~Convert.ToUInt16(flag));
case "UInt32": return (T)Enum.ToObject(origin.GetType(), Convert.ToUInt32(origin) & ~Convert.ToUInt32(flag));
case "UInt64": return (T)Enum.ToObject(origin.GetType(), Convert.ToUInt64(origin) & ~Convert.ToUInt64(flag));
}
throw new ArgumentException("T is invalid."); // ここたどり着かない
}
public static bool HasFlag<T>(this T origin, T flag)
{
if (!typeof(T).IsEnum)
throw new ArgumentException("T is not enum.");
var type = Enum.GetUnderlyingType(typeof(T));
switch (type.Name)
{
case "Byte": return (Convert.ToByte(origin) & Convert.ToByte(flag)) != Convert.ToByte(flag);
case "Int16": return (Convert.ToInt16(origin) & Convert.ToInt16(flag)) != Convert.ToInt16(flag);
case "Int32": return (Convert.ToInt32(origin) & Convert.ToInt32(flag)) != Convert.ToInt32(flag);
case "Int64": return (Convert.ToInt64(origin) & Convert.ToInt64(flag)) != Convert.ToInt64(flag);
case "SByte": return (Convert.ToSByte(origin) & Convert.ToSByte(flag)) != Convert.ToSByte(flag);
case "UInt16": return (Convert.ToUInt16(origin) & Convert.ToUInt16(flag)) != Convert.ToUInt16(flag);
case "UInt32": return (Convert.ToUInt32(origin) & Convert.ToUInt32(flag)) != Convert.ToUInt32(flag);
case "UInt64": return (Convert.ToUInt64(origin) & Convert.ToUInt64(flag)) != Convert.ToUInt64(flag);
}
throw new ArgumentException("T is invalid."); // ここたどり着かない
}
static Type EnumToUnderlyingTypeWithCache(Type enumType)
{
Type type;
if (EnumToUnderlyingType.ContainsKey(enumType))
{
type = EnumToUnderlyingType[enumType];
}
else
{
type = Enum.GetUnderlyingType(enumType);
EnumToUnderlyingType[enumType] = type;
}
return type;
}
}
[Flags]
public enum BitHoge : long
{
a = 0x0001,
b = 0x0002,
c = 0x0004,
d = 0x0008
}
@taross-f
Copy link
Author

taross-f commented Jul 12, 2016

Added HasFlag() for .NET3.5( on Unity).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment