7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】ジェネリックの enum を整数型に(高速に)キャストする

7
Last updated at Posted at 2025-09-02

やろうとしたこと

こんなシチュエーションです。
色々調べましたが、列挙型から整数へのキャスト自体、このやり方だと出来ないっぽい?

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);
    }
}
7
2
2

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
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?