#コード
Cast.cs
public static class Cast<T>
{
private static class Cache<S>
{
static Cache()
{
var p = Expression.Parameter(typeof(S));
var c = Expression.ConvertChecked(p, typeof(T));
Caster = Expression.Lambda<Func<S, T>>(c, p).Compile();
}
internal static readonly Func<S, T> Caster;
}
public static T From<S>(S source)
{
return Cache<S>.Caster(source);
}
}
↑引用元:http://csharpvbcomparer.blogspot.com/2014/09/net-cast-cheats-compiler.html
HasFlag.cs
public static bool HasFlagNoBoxing<T>(this T value, T flag) where T : struct, Enum
{
var intValue = Cast<int>.From(value);
var intFlagValue = Cast<int>.From(flag);
return (intValue & intFlagValue) != 0;
}
#パフォーマンス
計測コード.cs
[Flags]
public enum Number : int
{
One = 1,
Two = 1 << 1,
Three = 1 << 2,
Four = 1 << 3,
Five = 1 << 4,
}
var values = Enum.GetValues(typeof(Number)) as Number[];
Number result = default;
var count = 100000;
Profiler.BeginSample(Generic);
for (int i = 0; i < count; i++)
{
if (values[i % values.Length].HasFlagNoBoxing(Number.One | Number.Five))
{
result |= values[i % values.Length];
}
}
Profiler.EndSample();
初回はCompile()のせいでめちゃくちゃ重い
キャッシュされていれば使えなくも無い程度、調べてないけどiOSとかだと実行時にたぶん死ぬ
下記のコードはプロファイラ表示のConfrimedTypeMethodで、計測するとだいたい3倍くらい早いので
正直テンプレートかスニペットで用意するのが楽そう
Extentions.cs
public static class NumberExtention
{
public static bool HasFlagNoBoxing(this Number value, Number flag)
{
return (value & flag) != 0;
}
}
#試したけど駄目だったやつ
構造体でも制約指定してさえいればBox化せずにInterfaceを使えるのでいけるかと思いきや
Enumはだめで普通にIConvertible型に変換されてボックス化しました
Extentions.cs
public static bool hogehoge<T>(this T value,T flag) where T: struct, Enum, IConvertible
{
var intValue = value.ToInt32(null);
var intFlagValue = flag.ToInt32(null);
return (intValue & intFlagValue) != 0;
}
enumの基底にintを指定しても変換できず全部false
Extentions.cs
public static bool hogehoge<T>(this T value, T flag) where T : struct, Enum
{
if (value is int intValue && flag is int intFlagValue)
{
return (intValue & intFlagValue) != 0;
}
return false;
}
これでintに変換して判定しようとしたが
ジェネリックでLayoutKind.Explicitは例外を吐いて使用できず終了
FastEnumConvert.cs
[StructLayout(LayoutKind.Explicit)]
struct FastEnumConverter<T> where T : IConvertible
{
[FieldOffset(0)] public T Raw;
[FieldOffset(0)] public sbyte AsSByte;
[FieldOffset(0)] public byte AsByte;
[FieldOffset(0)] public short AsShort;
[FieldOffset(0)] public ushort AsUShort;
[FieldOffset(0)] public int AsInt;
[FieldOffset(0)] public uint AsUInt;
[FieldOffset(0)] public long AsLong;
[FieldOffset(0)] public ulong AsULong;
}
public static class FastEnumConvert
{
public static sbyte ToSByte<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsSByte; }
public static byte ToByte<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsByte; }
public static short ToShort<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsShort; }
public static ushort ToUShort<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsUShort; }
public static int ToInt32<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsInt; }
public static uint ToUInt32<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsUInt; }
public static long ToLong<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsLong; }
public static ulong ToULong<T>(T value) where T : IConvertible { return new FastEnumConverter<T> { Raw = value }.AsULong; }
public static T ToEnum<T>(sbyte value) where T : IConvertible { return new FastEnumConverter<T> { AsSByte = value }.Raw; }
public static T ToEnum<T>(byte value) where T : IConvertible { return new FastEnumConverter<T> { AsByte = value }.Raw; }
public static T ToEnum<T>(short value) where T : IConvertible { return new FastEnumConverter<T> { AsShort = value }.Raw; }
public static T ToEnum<T>(ushort value) where T : IConvertible { return new FastEnumConverter<T> { AsUShort = value }.Raw; }
public static T ToEnum<T>(int value) where T : IConvertible { return new FastEnumConverter<T> { AsInt = value }.Raw; }
public static T ToEnum<T>(uint value) where T : IConvertible { return new FastEnumConverter<T> { AsUInt = value }.Raw; }
public static T ToEnum<T>(long value) where T : IConvertible { return new FastEnumConverter<T> { AsLong = value }.Raw; }
public static T ToEnum<T>(ulong value) where T : IConvertible { return new FastEnumConverter<T> { AsULong = value }.Raw; }
}