やろうとしたこと
こんなシチュエーションです。
色々調べましたが、列挙型から整数へのキャスト自体、このやり方だと出来ないっぽい?
internal abstract class Hoge<T>
where T : Enum
{
private int ToInt(T value)
=> (int)value; // エラー
}
解決方法
「整数に変換するメソッド」をクラス内で使いたいだけだったので、
そのメソッドを抽象にして、派生クラスで定義してもらうようにしました。
internal abstract class Hoge<T>
where T : Enum
{
private protected abstract int ToInt(T value);
// ToInt() を使う
}
コメントからのアドバイス
Unsafe.BitCast が使える
公式リファレンス
列挙型のサイズが決まっているならば、このメソッドが有用。
自分のプロジェクトではbyteで統一しているので、↓↓こんな感じの拡張メソッドにしてみました。
internal static class MyExtension
{
internal static byte ToByteInteger<TEnum>(this TEnum value)
where TEnum : Enum
=> Unsafe.BitCast<TEnum, byte>(value);
}
追記
Static Type Caching
というテクニックを知りました。
汎用的な構成として、こんなクラスを作り・・・
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
public static class EnumCache<T> where T : unmanaged, Enum
{
public static readonly Type EnumType = typeof(T);
public static readonly Type UnderlyingType = Enum.GetUnderlyingType(typeof(T));
public static readonly T[] Values = (T[])Enum.GetValues(typeof(T));
public static readonly int Count = Values.Length;
public static readonly bool IsByte = UnderlyingType == typeof(byte);
public static readonly bool IsSByte = UnderlyingType == typeof(sbyte);
public static readonly bool IsShort = UnderlyingType == typeof(short);
public static readonly bool IsUShort = UnderlyingType == typeof(ushort);
public static readonly bool IsInt = UnderlyingType == typeof(int);
public static readonly bool IsUInt = UnderlyingType == typeof(uint);
public static readonly bool IsLong = UnderlyingType == typeof(long);
public static readonly bool IsULong = UnderlyingType == typeof(ulong);
public static IEnumerable<T> Enumerate()
{
return Values;
}
public static bool Contains(T value)
{
for (int i = 0; i < Values.Length; i++)
{
if (EqualityComparer<T>.Default.Equals(Values[i], value))
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long ToInt64Fast(T value)
{
if (IsByte)
return Unsafe.As<T, byte>(ref value);
if (IsSByte)
return Unsafe.As<T, sbyte>(ref value);
if (IsShort)
return Unsafe.As<T, short>(ref value);
if (IsUShort)
return Unsafe.As<T, ushort>(ref value);
if (IsInt)
return Unsafe.As<T, int>(ref value);
if (IsUInt)
return Unsafe.As<T, uint>(ref value);
if (IsLong)
return Unsafe.As<T, long>(ref value);
if (IsULong)
return unchecked((long)Unsafe.As<T, ulong>(ref value));
throw new NotSupportedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ulong ToUInt64Fast(T value)
{
if (IsByte)
return Unsafe.As<T, byte>(ref value);
if (IsSByte)
return unchecked((ulong)Unsafe.As<T, sbyte>(ref value));
if (IsShort)
return unchecked((ulong)Unsafe.As<T, short>(ref value));
if (IsUShort)
return Unsafe.As<T, ushort>(ref value);
if (IsInt)
return unchecked((ulong)Unsafe.As<T, int>(ref value));
if (IsUInt)
return Unsafe.As<T, uint>(ref value);
if (IsLong)
return unchecked((ulong)Unsafe.As<T, long>(ref value));
if (IsULong)
return Unsafe.As<T, ulong>(ref value);
throw new NotSupportedException();
}
}
・・・こんなふうに使うことができる。
便利!
internal abstract class Base<T>
where T : unmanaged, Enum
{
protected static int Count => EnumCache<T>.Count;
protected static T[] Values => EnumCache<T>.Values;
protected static long ToInt64(T value)
{
return EnumCache<T>.ToInt64Fast(value);
}
}