Yuta.NET

C#の属性を用いたenumの小技


以前、拡張メソッドを用いて列挙子と対となる文字列の取得方法を紹介しましたが、可読性がより高まるDisplay属性を使う方法を紹介します。

ただ、欠点としては遅いです。以前、紹介したコードと比べて約500倍ぐらいの差が出ます。しかし、大量に参照しない限りは誤差の範疇かと思います。実験では100万回参照して、約2000msでしたので許容範囲かと思われます。

とは言え、許容出来ない場合もあると思いますので、工夫をするか以前、紹介した「C# 8.0の拡張メソッドを用いたenumの小技」の方が良いでしょう。

やり方はenumの個々のメンバ上にDisplay属性を宣言するだけでOK

サンプルではシンプルに名称を取得していますが、リソースのキーをDisplay属性で指定して、リソースファイルから名称を取得するってことも可能です。


/// <summary>
/// 曜日
/// </summary>
public enum Week
{
    /// <summary>
    /// 
    /// </summary>
    [Display(Name = "日")]
    Sunday = DayOfWeek.Sunday,
    /// <summary>
    /// 
    /// </summary>
    [Display(Name = "月")]
    Monday = DayOfWeek.Monday,
}

Display属性の取得には拡張メソッドを用いると便利ですね、下記ではジェネリックを使うことにより、全てのenumに対応可能としています。


/// <summary>
/// Enum拡張メソッド用クラス
/// </summary>
public static class EnumExtensions
{
    /// <summary>
    /// Enumに定義されているDisplay属性を取得
    /// </summary>
    /// <typeparam name="T">enumの型</typeparam>
    /// <param name="e">enum</param>
    /// <returns>Display属性</returns>
    public static DisplayAttribute? GetDisplayAttribute<T>(this T e) where T : Enum => e.GetType().GetField(e.ToString())?.GetCustomAttribute<DisplayAttribute>();
}

下記、全文です。上手く記述することで可読性が上がりそうです。脱スパゲッティ!!!


using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace YutaCore.ConsoleApp1
{
    /// <summary>
    /// 曜日
    /// </summary>
    public enum Week
    {
        /// <summary>
        /// 
        /// </summary>
        [Display(Name = "日")]
        Sunday = DayOfWeek.Sunday,
        /// <summary>
        /// 
        /// </summary>
        [Display(Name = "月")]
        Monday = DayOfWeek.Monday,
        /// <summary>
        /// 
        /// </summary>
        [Display(Name = "火")]
        Tuesday = DayOfWeek.Tuesday,
        /// <summary>
        /// 
        /// </summary>
        [Display(Name = "水")]
        Wednesday = DayOfWeek.Wednesday,
        /// <summary>
        /// 
        /// </summary>
        [Display(Name = "木")]
        Thursday = DayOfWeek.Thursday,
        /// <summary>
        /// 
        /// </summary>
        [Display(Name = "金")]
        Friday = DayOfWeek.Friday,
        /// <summary>
        /// 
        /// </summary>
        [Display(Name = "土")]
        Saturday = DayOfWeek.Saturday,
    }

    /// <summary>
    /// Enum拡張メソッド用クラス
    /// </summary>
    public static class EnumExtensions
    {
        /// <summary>
        /// Enumに定義されているDisplay属性を取得
        /// </summary>
        /// <typeparam name="T">enumの型</typeparam>
        /// <param name="e">enum</param>
        /// <returns>Display属性</returns>
        public static DisplayAttribute? GetDisplayAttribute<T>(this T e) where T : Enum => e.GetType().GetField(e.ToString())?.GetCustomAttribute<DisplayAttribute>();
    }

    class Program
    {
        static void Main(string[] args)
        {
            string name = string.Empty;
            // 整形用に最大文字数を取得(注:この記述のLinqは.NET3.5以上で使用可)
            int maxLength = Enum.GetNames(typeof(Week)).Max(v => v.Length);

            Week value;

            // foreachでEnumの全ての列挙子(値)を取得
            foreach (var item in Enum.GetNames(typeof(Week)))
            {
                if (!Enum.TryParse<Week>(item, out value)) continue;
                // 固定幅で名称を取得
                name = string.Format("{0, -" + maxLength + "}", value.ToString());
                Console.WriteLine("{0}:{1} : {2}曜日", (int)value, name, value.GetDisplayAttribute()?.Name);
            }
            Console.ReadKey(true);
        }
    }
}