Enumを(Javaのように)別の値を持たせるなど拡張するってメモメモ

Enumに文字列を与えたいというのは少なくなくよくある。
例えばFruits.Appleには.ToString()したら「リン ゴ」と出て欲しいなー、とか。
それならFruits.リンゴと、日本語名つければ?というのはごもっとも。
でも、同時に「林檎」とも付けたいなー、とかも 思ってしまったりするわけです。しません?Java→C#な人が一番不満に思うのはEnumのようですし(JavaのEnumは高機能!)。

例えばこんな風にかけたらいいな、って。

ヤバい、無くなる前にメモメモ

 

// こうやって属性定義するだけ!
public enum Color
{
[Japanese(“黒”), Hex(“000000”), Rgb(0, 0, 0)]
Black,
[Japanese(“白”), Hex(“FFFFFF”), Rgb(255, 255, 255)]
White,
[Japanese(“赤”), Hex(“FF0000”), Rgb(255, 0, 0)]
Red
}

class Program
{
static void Main(string[] args)
{
var red = Color.Red;
Console.WriteLine(red.ToHex()); // FF0000
Console.WriteLine(red.ToJpnName()); // 赤
Console.WriteLine(red.ToRgb()); // 255000000
}
}

んね、非常にすっきり定義出来て幸せ度高い。これをもし普通に書くならば

interface IColor
{
string EngName { get; }
string JpnName { get; }
string Rgb { get; }
string Hex { get; }
}

public class Red : IColor
{
public string EngName { get { return “Red”; } }
public string JpnName { get { return “赤”; } }
public string Rgb { get { return “255000000”; } }
public string Hex { get { return “FF0000”; } }
}

といった感じになって面倒くさいことこの上ない(いや別にこれクラスの意味あんまなくてnew Color(”Red”, “赤”,..)といった感じにインスタンスでいいやん、という感じではありますががが。Enumはswitch要因なとこがメインなので、そもそもColorという例が良くないかしら…) まあともかく、Enumは素晴らしい。属性もまた素晴らしい。んで、Enumへの別名などの定義の仕組みは非常に単純で、個別のEnumへ拡張メソッドを定義しているだけです。Enumと拡張メソッドは相性が良い。

public static class ColorExtensions
{
private static Dictionary<Color, RgbAttribute> rgbCache;
private static Dictionary<Color, HexAttribute> hexCache;
private static Dictionary<Color, JapaneseAttribute> jpnCache;

static ColorExtensions()
{
// Enumから属性と値を取り出す。
// この部分は汎用的に使えるようユーティリティクラスに隔離してもいいかもですね。
var type = typeof(Color);
var lookup = type.GetFields()
.Where(fi => fi.FieldType == type)
.SelectMany(fi => fi.GetCustomAttributes(false),
(fi, Attribute) => new { Color = (Color)fi.GetValue(null), Attribute })
.ToLookup(a => a.Attribute.GetType());

// キャッシュに突っ込む
jpnCache = lookup[typeof(JapaneseAttribute)].ToDictionary(a => a.Color, a => (JapaneseAttribute)a.Attribute);
hexCache = lookup[typeof(HexAttribute)].ToDictionary(a => a.Color, a => (HexAttribute)a.Attribute);
rgbCache = lookup[typeof(RgbAttribute)].ToDictionary(a => a.Color, a => (RgbAttribute)a.Attribute);
}

public static string ToJpnName(this Color color)
{
return jpnCache[color].Value;
}

public static string ToHex(this Color color)
{
return hexCache[color].Value;
}

public static string ToRgb(this Color color)
{
var rgb = rgbCache[color];
return string.Format(“{0:D3}{1:D3}{2:D3}”, rgb.R, rgb.G, rgb.B);
}
}

// 属性などり
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class JapaneseAttribute : Attribute
{
public string Value { get; private set; }

public JapaneseAttribute(string value)
{
Value = value;
}
}

[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class RgbAttribute : Attribute
{
public int R { get; private set; }
public int G { get; private set; }
public int B { get; private set; }

public RgbAttribute(int r, int g, int b)
{
R = r;
G = g;
B = b;
}
}

[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class HexAttribute : Attribute
{
public string Value { get; private set; }

public HexAttribute(string value)
{
Value = value;
}
}

良い情報助かるーー

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です