はじめに
始めまして、エンジニアの朴・小林です。
ここでは保守性が高まる.NETのAttribute(アトリビュート)について説明していきます。
Attribute(アトリビュート)とは?
属性とも呼ばれる、クラス・構造体、または、そのメンバー(メゾット・プロパティなど)に対して付与できる追加情報です。属性をプログラム要素に関連付けると、リフレクションと呼ばれる手法を使用して、実行時にその属性を照会することができます。
例えば:PersonというクラスにHand、Foot、Face等のプロパティが存在し、Walk(),Eat()等のメソッドが存在する。
Personというクラスに[police]というAttributeを付けると、このPersonが警察だとわかる、同様に[engineer]を付けると、このPersonがエンジニアだと分かるというイメージを持っていただければと思います。
Attributeの使い方
■Attributeはクラス Attributeを使用する場合、必ずSystem.Attribute.を継承する必要がある
■Attributeの名前 一般的に、〇〇Attributeとする。 利用する際にAttributeを省略することができる。
例:TestAttributeは利用時に[Test]と省略ができる
アトリビュートを実装してみる
例えば、あるクラスのUserが下記の状態だとする。
Normalの場合は「通常ユーザー」を出力
Frozenの場合は「利用中止ユーザー」を出力
Deletedの場合は「退会ユーザー」を出力
public enum UserState
{
Nomal = 0, //通常
Frozen = 1, //利用できない
Deleted = 2 //退会済
}
一般的に、下記の様なswitch文のロジックを組むと思いますが、
これだと、状態を追加するほど、分岐の条件も増やさないといけないので、保守性も非常い低いです。
ここでAttributeを利用すればこれらの事が解決できます。
switch(state)
{
case UserState.Nomal:
Console.WriteLine("通常ユーザー");
break;
case User.Frozen:
Console.WriteLine("利用中止ユーザー");
break;
case User.Deleted:
Console.WriteLine("退会ユーザー");
break;
}
↓Attributeを使用すると
//アトリビュートクラスを作成
public class RemarkAttribute : Attribute
{
public RemarkAttribute(string remark)
{
this._Remark = remark;
}
private string _Remark = null;
public string GetRemark()
{
return this._Remark;
}
}
//Remarkのアトリビュートを追加
public enum UserState
{
[Remark("通常ユーザー")]
Nomal = 0, //通常
[Remark("利用中止ユーザー")]
Frozen = 1, //利用できない
[Remark("退会ユーザー")]
Deleted = 2 //退会済
}
//アトリビュートの機能を作成
public static class RemarkExtension
{
public static string GetRemark(this Enum value)
{
//反射、リフレクションを利用。ネットで調べてください
//valueのタイプを取得
Type type = value.GetType();
//フィールドを取得
FieldInfo field = type.GetField(value.ToString());
//フィールドにRemarkAttributeが定義されていれば
if (field.IsDefined(typeof(RemarkAttribute), true))
{
RemarkAttribute attribute =
(RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true);
return attribute.GetRemark();
}
else
{
return value.ToString();
}
}
}
上記を実現すると、下記のようなコードがいらなくなる。
switch(state)
{
case UserState.Nomal:
Console.WriteLine("通常ユーザー");
break;
case User.Frozen:
Console.WriteLine("利用中止ユーザー");
break;
case User.Deleted:
Console.WriteLine("退会ユーザー");
break;
}
↓
//stateがRemarkアトリビュートがある場合、アトリビュートのRemarkをそのまま取得できる、ない場合は、stateそのままになる。
Console.WriteLine(state.GetRemark());
また、UserクラスにAgeというプロパティが存在していて、データベースへ挿入する際に、年齢が18から30までのユーザーしか挿入させないように制限したい。
まず、AgeValidationAttributeというクラスを宣言
public class AgeValidationAttribute : Attribute
{
public AgeValidationAttribute(int min, int max)
{
this._Min = min;
this._Max = _Max;
}
private int _Min = 0;
private int _Max = 0;
//バリデーション
public bool Validate(object value)
{
if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
{
if (int.TryParse(value.ToString(), out int resut))
{
if (resut > this._Min && resut < this._Max)
{
return true;
}
}
}
return false;
}
}
次はアトリビュートの機能を実現するためのStaticメソッドを宣言
public static class ValidateExtension
{
public static bool Validate(this object value)
{
Type type = value.GetType();
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(AgeValidationAttribute), true))
{
AgeValidationAttribute attribute =
(AgeValidationAttribute)prop.GetCustomAttribute(typeof(RemarkAttribute), true);
if (attribute.Validate(prop.GetValue(value)) == false)
{
return false;
}
}
}
return true;
}
}
最後に、プロパティAgeにAgeValidationアトリビュートを追加
[AgeValidation(18,30)]
public int Age { get; set; }
利用時
user.Validate();
最後に
アトリビュートを使用すると保守性の高いコードを実現することができます。 是非利用してみてください。
コメント