0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Unity]ボックス化しないジェネリックなEnumのHasFlag

Posted at

#コード

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()のせいでめちゃくちゃ重い
HasFlag.JPG
キャッシュされていれば使えなくも無い程度、調べてないけどiOSとかだと実行時にたぶん死ぬ
hasflag2.JPG

下記のコードはプロファイラ表示の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; }
}

↑引用元http://baba-s.hatenablog.com/entry/2017/12/27/210400

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?