C#でカスタム属性と、拡張メソッド(Enum(列挙体)に属性を追加)
今日は、C#でカスタム属性と、拡張メソッドのお話です。
C#で、Enumに付随する情報を持つ場合に、どのように実装していますか?
Switch文や、IF文で、Enumの情報を返すメソッドを書いたりしていませんか。
ついでに、Enum値を追加したのに、Switch文に追加を忘れて、バグ!!!なんてなったことありませんか。
そんな時には、これ!
本日限りの大特価です!!!
Enumの個所に情報を書いてしまいましょう。(多少実行速度は落ちますが・・・)
namespace Sample
{
//===========================================================
/// <summary>
/// 情報保持用カスタム属性
/// </summary>
//===========================================================
[AttributeUsage(System.AttributeTargets.Field)]
public class DescriptionAttribute : Attribute
{
public String Name { get; set; }
public Int32 Price { get; set; }
public DescriptionAttribute(String name, Int32 price)
{
this.Name = name;
this.Price = price;
}
}
/// <summary>
//===========================================================
/// フルーツ列挙体
/// </summary>
//===========================================================
public enum Fruits
{
[Description("メロン", 10000)]
Melon,
[Description("みかん", 100)]
Orange,
[Description("りんご", 300)]
Apple,
}
//===========================================================
/// <summary>
/// フルーツ列挙体用拡張メソッド
/// </summary>
//===========================================================
public static class FruitsExtension
{
public static R GetProperty<R, V, T>(V enumValue, String propertyName)
where T : Attribute
{
var val = default(R);
var enumType = enumValue.GetType();
var enumName = Enum.GetName(enumType, enumValue);
var attrs = enumType.GetField(enumName).GetCustomAttributes(typeof(T), false);
foreach (Object attr in attrs)
{
var description = null as T;
description = attr as T;
if (description != null)
{
val = (R)description.GetType().GetProperty(propertyName).GetValue(description);
break;
}
}
return val;
}
public static String Name(this Fruits fruits)
{
return GetProperty<String, Fruits, DescriptionAttribute>(fruits, "Name");
}
public static Int32 Price(this Fruits fruits)
{
return GetProperty<Int32, Fruits, DescriptionAttribute>(fruits, "Price");
}
}
//===========================================================
/// <summary>
/// 実行サンプル
/// </summary>
//===========================================================
public class Sample
{
public void Main(string[] args)
{
ViewFruits(Fruits.Melon);
ViewFruits(Fruits.Orange);
ViewFruits(Fruits.Apple);
}
public void ViewFruits(Fruits furits)
{
var name = furits.Name();
var price = furits.Price();
Console.WriteLine(String.Format("フルーツ:{0}\t値段:{1}",name, price));
}
}
}
解説
カスタム属性
[AttributeUsage(System.AttributeTargets.Field)]カスタム属性のクラスです。Enumの項目に表示用の名前(Name)と値段(Price)を持たせるようにしています。
public class DescriptionAttribute : Attribute
{
public String Name { get; set; }
public Int32 Price { get; set; }
public DescriptionAttribute(String name, Int32 price)
{
this.Name = name;
this.Price = price;
}
}
列挙体へカスタム属性を追加
Enumに属性を追加しています。
public enum Fruits
{
[Description("メロン", 10000)]
Melon,
[Description("みかん", 100)]
Orange,
[Description("りんご", 300)]
Apple,
}
こうしておけば、新しくEnum値を追加するときでも忘れることはありません。
ここまでしておいて、属性を追加しない人は、意図的に不具合を埋め込んで
残業代を稼ごうとしているせこい人しかいません。
拡張メソッド
public static class FruitsExtensionこの拡張メソッドは、作成しなくてもよいかもしれませんが、毎回GetCustomAttributesってやるのが面倒なので、書いています。ジェネリックのGetPropertyは、Enum用に汎かしていますので、
{
public static R GetProperty<R, V, T>(V enumValue, String propertyName)
where T : Attribute
{
var val = default(R);
var enumType = enumValue.GetType();
var enumName = Enum.GetName(enumType, enumValue);
var attrs = enumType.GetField(enumName).GetCustomAttributes(typeof(T), false);
foreach (Object attr in attrs)
{
var description = null as T;
description = attr as T;
if (description != null)
{
val = (R)description.GetType().GetProperty(propertyName).GetValue(description);
break;
}
}
return val;
}
public static String Name(this Fruits fruits)
{
return GetProperty<String, Fruits, DescriptionAttribute>(fruits, "Name");
}
public static Int32 Price(this Fruits fruits)
{
return GetProperty<Int32, Fruits, DescriptionAttribute>(fruits, "Price");
}
}
別のカスタム属性を書く場合でも、利用可能です。
0 件のコメント:
コメントを投稿