8
6

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.

Enumにカスタム属性を使ってint型以外の値を返せるようにする

Last updated at Posted at 2017-02-21

いつ、どこで書いたかすっかり忘れたけれど、
頭の片隅から消えないうちに書き留めておきたいと思います。

上から順に以下の物を作っていきます。

  1. カスタム属性
  2. 拡張メソッド
  3. Enumの定義
  4. 使用感

はじめは、準備から

1. カスタム属性

Ready.cs
using System;

[AttributeUsage( AttributeTargets.Field )]
public class SubValueAttribute : Attribute
{
    public SubValueAttribute( object value )
    {
        this.SubValue = value;
    }

    public object SubValue { get; private set; }
}

作りはシンプルで、Attributeを継承したクラスを作り、アクセサとなるプロパティとミューテータを用意してあげればいいだけです。
今回はEnumのフィールドに付けたいので適用対象を「AttributeTargets.Field」としています。

2. 拡張メソッド

Ready.cs
using System.Linq;
using System.Reflection;

public static class EnumEx
{
    public static T ToSubValue<T>( this Enum enm )
    {
        return (T) enm.GetType()
           .GetRuntimeField( enm.ToString() )
           .GetCustomAttributes( typeof( SubValueAttribute ), false )
           .Cast<SubValueAttribute>()
           .Single()
           .SubValue;
    }
}

長々とチェーンメソッドで書いていますが、やっている事はフィールドに設定した属性の値を任意(T)の型にキャストして返しているだけです。

3. Enumの定義

TrySample.cs

// こんな形でEnumに属性を付けます。
enum Example
{
    [SubValue( "ABCDEFG" )]
    StringValue = 0,

    [SubValue( 12345 )]
    IntValue,

    [SubValue( new[] { "A", "B", "C" } )]
    ArrayValue,
}

作っておいた属性を装着して、設定値を定義しておきます。

ここまでが準備で、後はこれらを使うだけになります。

4. 使用感

TrySample.cs
static void Main()
{
    // こんな感じで取れます
    string   stringValue     = Example.StringValue.ToSubValue<string>();
    int      intValue        = Example.IntValue.ToSubValue<int>();
    string[] arrayValue      = Example.ArrayValue.ToSubValue<string[]>();

    // 出力
    Console.WriteLine( "StringValue \t= {0}", stringValue );
    Console.WriteLine( "IntValue \t= {0}", intValue );
    Console.WriteLine( "ArrayValue \t= {0},{1},{2}", arrayValue[0], arrayValue[1], arrayValue[2] );

    // 結果:
    // StringValue     = ABCDEFG
    // IntValue        = 12345
    // ArrayValue      = A,B,C
}

拡張メソッドをジェネリックにしていますが、「毎回毎回、型を指定するのがめんどう!」「そんなに汎用性を求めてない!」という方は、必要に応じてカスタム属性と拡張メソッドをstring型やint型で型を限定して作って置くのもいいかもしれません。

その際はこんな感じなると思います。

Ready.cs
using System;
using System.Linq;
using System.Reflection;

// カスタム属性
[AttributeUsage( AttributeTargets.Field )]
public class StringAttribute : Attribute
{
    public StringAttribute( string value )
    {
        this.String = value;
    }

    public string String { get; private set; }
}

// 拡張メソッド
public static class EnumEx
{
    public static string ToStringFromSubValue( this Enum enm )
    {
        return enm.GetType()
           .GetRuntimeField( enm.ToString() )
           .GetCustomAttributes( typeof( StringAttribute ), false )
           .Cast<StringAttribute>()
           .Single()
           .String;
    }
}

そうすれば、多少なりコーディングが楽なるかも?

TrySample.cs
enum Example
{
    [StringValue( "ABCDEFG" )]
    StringValue = 0,
}

static void Main()
{
    // こんな感じで取れます
    //string   stringValue   = Example.StringValue.ToSubValue<string>();
    string   stringValue     = Example.StringValue.ToStringFromSubValue();
    
    // 出力
    Console.WriteLine( "StringValue \t= {0}", stringValue );
     
    // 結果:
    // StringValue     = ABCDEFG
}

もっとスッキリさせたいが、力量不足なのでこの辺で妥協してます。

8
6
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
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?