📜 ⬆️ ⬇️

Simulating properties for enumeration elements (Enumerations) in the .NET Framework 3.5

I'm sure many people occasionally came across the need to specify some simple properties for enumeration elements in C #.

There are various ways to solve this problem, the most popular of which, in my opinion, is to write an auxiliary static method that takes an enumeration as a parameter and returns the result of the desired type.

I want to offer you a slightly different, more universal and elegant way, based on attributes, methods of expansion and reflection.

Attributes provide an efficient method for associating declarative information with C # code (types, methods, properties, etc.). An attribute associated with a program entity may be requested at runtime using a method called reflection.
© MSDN, http://msdn.microsoft.com/ru-ru/library/z0w1kczw.aspx
')
Reflection (reflection) is used to dynamically create an instance of a type, bind a type to an existing object, as well as get a type from an existing object and dynamically invoke its methods or access its fields and properties.
© MSDN, http://msdn.microsoft.com/ru-ru/library/ms173183.aspx

Extension methods allow you to “add” methods to existing types without creating a new derived type, recompiling or otherwise modifying the original type. Extension methods are a special kind of static method, but they are called as if they were instance methods in the extended type.
© MSDN, http://msdn.microsoft.com/ru-ru/library/bb383977.aspx


Look at how visually we can describe the attributes for the elements of enumerations:

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
}

... and how simple we can get the values ​​of these elements:

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


Implementation



Let's create an abstract class BaseAttribute from which our own attributes will be inherited from now on. The designer accepts and places the object for storage. The GetValue () method returns a stored object.

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

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

Next, we need to create an extension method, which for the element of any enumeration will return the value of the required attribute. To do this, create a static class EnumAttributesBaseLogic and define our method in it, which we call GetAttributeValue . The last argument of the method is the value that will be returned if the transferred element of the enumeration is not marked with the passed attribute.

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();
}
}

That's all! Now we can create the attributes we need in just one line. See how easy it is to create a Weight attribute * :

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

* Remember that you can use only constants or arrays of primitive types as attribute arguments.

Also just now we can create an extension method to read the value of the attribute we just created:

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

For convenience, I recommend placing your own attribute definitions and a static class with advanced methods for reading attributes into a separate file:

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);
}
}
}

A working example can be downloaded from here:
http://www.googman.ru/sources/enumattributesdemo.zip (9.1 kb)

PS Do not judge strictly - my first post.

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


All Articles