.NET Framework 3.5の列挙のプロパティをシミュレートする

私は、C#の列挙要素にいくつかの単純なプロパティを指定する必要性に定期的に遭遇していると確信しています。

この問題を解決するにはさまざまな方法がありますが、私の意見では、最も一般的なのは、列挙をパラメーターとして受け取り、目的の型の結果を返す補助静的メソッドを作成することです。

属性、拡張方法、リフレクションに基づいて、少し異なる、より普遍的でエレガントな方法を提供したいと思います。

属性は、宣言情報をC#コード(型、メソッド、プロパティなど)に関連付けるための効率的な方法を提供します。 プログラムエンティティに関連付けられた属性は、リフレクションと呼ばれるメソッドを使用して実行時に要求できます。
©MSDN、 http://msdn.microsoft.com/en-us/library/z0w1kczw.aspx

リフレクション (リフレクション)は、型のインスタンスを動的に作成し、その型を既存のオブジェクトにバインドし、既存のオブジェクトから型を取得し、そのメソッドを動的に呼び出したり、フィールドやプロパティにアクセスしたりするために使用されます。
©MSDN、 http://msdn.microsoft.com/en-us/library/ms173183.aspx

拡張メソッドを使用すると、新しい派生型を作成、再コンパイル、または元の型を変更せずに、既存の型にメソッドを「追加」できます。 拡張メソッドは、特別な種類の静的メソッドですが、拡張型のインスタンスメソッドであるかのように呼び出されます。
©MSDN、 http://msdn.microsoft.com/en-us/library/bb383977.aspx


列挙要素の属性をどの程度明確に記述できるかを見てください。

public enum Womans
{
[Age(25)]
[Weight(54.5)]
Masha,

[Age(32)]
[Weight(102.5)]
Lena,

[Age(44)]
[Weight(77.4)]
Ira,

[Age(28)]
[Weight(63.75)]
Fekla
}

public enum Status
{
[RussianName( "" )]
Opened = 100,

[RussianName( "" )]
Closed = 200,

[RussianName( "- " )]
AnythingElse = 500
}

...およびこれらの要素の値を取得する方法

double iraWeight = Womans.Ira.GetWeight();
int lenaAge = Womans.Lena.GetAge();
string closedName = Status.Closed.GetRussianName();


実装



独自の属性が将来継承される抽象BaseAttributeクラスを作成しましょう。 コンストラクターは、オブジェクトを受け入れて保管します。 GetValue()メソッドは、保存されたオブジェクトを返します。

public abstract class BaseAttribute : Attribute
{
private readonly object _value;
public BaseAttribute( object value ) { this ._value = value ; }

public object GetValue() { return this ._value; }
}

次に、拡張メソッドを作成する必要があります。これは、列挙の要素に対して、必要な属性の値を返します。 これを行うには、静的クラスEnumAttributesBaseLogicを作成し、その中にメソッドを定義します。このメソッドをGetAttributeValueと呼びます 。 メソッドの最後の引数は、渡された列挙要素が渡された属性でマークされていない場合に返される値です。

public static class EnumAttributesBaseLogic
{
public static VAL GetAttributeValue<ENUM, VAL>( this ENUM enumItem, Type attributeType, VAL defaultValue)
{
var attribute = enumItem.GetType().GetField(enumItem.ToString()).GetCustomAttributes(attributeType, true )
.Where(a => a is BaseAttribute)
.Select(a => (BaseAttribute)a)
.FirstOrDefault();

return attribute == null ? defaultValue : (VAL)attribute.GetValue();
}
}

それだけです! これで、必要な属性を1行で作成できます。 Weight *属性を単純に作成する方法を見てください。

public class Weight : BaseAttribute { public Weight( double value ) : base ( value ) { } }

*定数またはプリミティブ型の配列のみが属性引数として使用できることに注意してください。

また、作成した属性の値を読み取るための拡張メソッドを簡単に作成できます。

public static double GetWeight( this Enum enumItem)
{
return enumItem.GetAttributeValue( typeof (Weight), 0m);
}

便宜上、独自の属性定義と、属性を別のファイルに読み込むための高度なメソッドを持つ静的クラスを配置することをお勧めします。

using System;

namespace EnumAttributesDemo
{
public class Age : BaseAttribute { public Age( int value ) : base ( value ) { } }
public class Weight : BaseAttribute { public Weight( double value ) : base ( value ) { } }
public class RussianName : BaseAttribute { public RussianName( string value ) : base ( value ) { } }

public static class EnumExtensionMethods
{
public static int GetAge( this Womans enumItem)
{
return enumItem.GetAttributeValue( typeof (Age), 0);
}

public static double GetWeight( this Womans enumItem)
{
return enumItem.GetAttributeValue( typeof (Weight), 0d);
}

public static string GetRussianName( this Status enumItem)
{
return enumItem.GetAttributeValue( typeof (RussianName), string .Empty);
}
}
}

実例はここからダウンロードできます:
http://www.googman.ru/sources/enumattributesdemo.zip(9.1 Kb)

PS厳密に判断しないでください-私の最初の投稿。

Source: https://habr.com/ru/post/J68253/


All Articles