How to get the DescriptionAttribute value from an enum member - c#

I have a enum type
public enum DataType:int
{
None = 0,
[Description("A")]
Alpha = 1,
[Description("N")]
Numeric,
[Description("AN")]
AlphaNumeric,
[Description("D")]
Date
}
How do I retrieve the description attribute value of, say, Alpha.
Eg(ideal) : DataType.Alpha.Attribute should give "A"

Use this
private string GetEnumDescription(Enum value)
{
// Get the Description attribute value for the enum value
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}

I have an extension method to do just that:
public static string GetDescription(this Enum enumValue)
{
//Look for DescriptionAttributes on the enum field
object[] attr = enumValue.GetType().GetField(enumValue.ToString())
.GetCustomAttributes(typeof (DescriptionAttribute), false);
if (attr.Length > 0) // a DescriptionAttribute exists; use it
return ((DescriptionAttribute) attr[0]).Description;
//The above code is all you need if you always use DescriptionAttributes;
//If you don't, the below code will semi-intelligently
//"humanize" an UpperCamelCased Enum identifier
string result = enumValue.ToString();
//"FooBar" -> "Foo Bar"
result = Regex.Replace(result, #"([a-z])([A-Z])", "$1 $2");
//"Foo123" -> "Foo 123"
result = Regex.Replace(result, #"([A-Za-z])([0-9])", "$1 $2");
//"123Foo" -> "123 Foo"
result = Regex.Replace(result, #"([0-9])([A-Za-z])", "$1 $2");
//"FOOBar" -> "FOO Bar"
result = Regex.Replace(result, #"(?<!^)(?<! )([A-Z][a-z])", " $1");
return result;
}
Usage:
var description = DataType.Alpha.GetDescription(); //"A"
public enum TestEnums
{
IAmAComplexABCEnumValue,
}
//"I Am A Complex ABC Enum Value"
var complexCamelCasedDescription = TestEnums.IAmAComplexABCEnumValue.GetDescription();

I've created a custom generic method to get any attribute value from an enum.
public static class EnumHelper
{
public static string GetAttributeValueByEnumValue<EnumType, AttributeType>(EnumType value, string attributeProperty = null)
where EnumType : Enum
where AttributeType : Attribute
{
var enumField = value.GetType().GetField(value.ToString());
var enumAttributes = enumField?.GetCustomAttributes(typeof(AttributeType), false) as AttributeType[];
if (enumAttributes == null || !enumAttributes.Any())
throw new Exception($"Attribute [{typeof(AttributeType).Name}] not found in enum [{typeof(EnumType).Name}] type with value [{value.ToString()}]!");
var enumAttribute = enumAttributes.First();
var attributeValue = enumAttribute
.GetType()
?.GetProperty(!string.IsNullOrEmpty(attributeProperty) ? attributeProperty : typeof(AttributeType).Name.Replace("Attribute", string.Empty))
?.GetValue(enumAttribute);
if (attributeValue == null)
throw new Exception("Error reading enum attribute value!");
return attributeValue.ToString();
}
}
To use it, simply call it like below:
EnumHelper.GetAttributeValueByEnumValue<EnumLookup, DescriptionAttribute>(enumValue);
You can also provide the attributeProperty name in case of a custom enum, like below:
EnumHelper.GetAttributeValueByEnumValue<EnumLookup, TitleAttribute>(enumValue, "TitleName");
I hope it helps you :)

Related

Get value of property with reflection in C#

i need to get value of property in C# with reflection .
i need to find lenght of string and compare to max .
i write this code :
public static bool ValidateWithReflection(T model)
{
bool validate = false;
var cls = typeof(T);
PropertyInfo[] propertyInfos = cls.GetProperties();
foreach (PropertyInfo item in propertyInfos)
{
var max = item.GetCustomAttributes<MaxLenghtName>().Select(x => x.Max).FirstOrDefault();
if (max != 0)
{
var lenght = item.GetType().GetProperty(item.Name).GetValue(cls, null);
if ((int)lenght > max)
{
return validate = true;
}
}
}
return validate;
}
and this for get value of property :
var lenght = item.GetType().GetProperty(item.Name).GetValue(cls, null);
but it show me this error :
Message "Object does not match target type." string
now whats the problem ? how can i solve this problem ?
What is item.GetType().GetProperty(item.Name) supposed to do? item is a PropertyInfo instance. You're not looking to get properties of that, but of your model.
So simplify your code to this:
var value = item.GetValue(model) as string;
if (value?.Length > max)
{
return validate = true;
}

C# using [Flags] with [Description]

I'm looking to combine the use of enumerations - with the bitwise [Flags] - as well as using a description of the combination of the results. I've looked into examples here using the Flags Attribute, and using the Description Attribute, but not both.
Something like:
[Flags]
public enum ReasonCode
{
[Description("Verified")]
None = 0,
[Description("Check A")]
Reason1 = 1,
[Description("Check B")]
Reason2 = 2,
[Description("Check C")]
Reason3 = 4,
[Description("Check D")]
Reason4 = 8,
[Description("Check E")]
Reason5 = 16,
[Description("Check F")]
Reason6 = 32,
[Description("Check G")]
Reason7 = 64
}
I need to specify all reasons why there was a failure. Using "Reason1", etc... isn't descriptive enough for what I am looking for. I would need a much more verbose description, like "Reason1 - Check A".
For Example:
A value of 5 would be Reason1 and Reason3.
The description would then be:
Failure:
Reason1 - Check A.
Reason3 - Check C.
Is is possible to do combine descriptions like flags?
Here's a more generic extension method that I have used which will deal with all enum values including flag combinations:
public static string GetDescription(this Enum value)
{
//pull out each value in case of flag enumeration
var values = value.ToString().Split(',').Select(s => s.Trim());
var type = value.GetType();
return string.Join(" | ", values.Select(enumValue => type.GetMember(enumValue)
.FirstOrDefault()
?.GetCustomAttribute<DescriptionAttribute>()
?.Description
?? enumValue.ToString()));
}
If a description attribute is not present then it will simply return the value itself.
In order to produce the result you are looking for, use the following code:
public static String GetDescription(ReasonCode reasonCode)
{
if (reasonCode == ReasonCode.None)
return "Verified";
StringBuilder sb = new StringBuilder();
sb.AppendLine("Failed:");
foreach (ReasonCode rc in Enum.GetValues(typeof(ReasonCode)).Cast<ReasonCode>())
{
if (rc == ReasonCode.None)
continue;
if (reasonCode.HasFlag(rc))
sb.AppendLine(rc.ToString() + " - " + GetEnumDescription(rc));
}
return sb.ToString();
}
The code used to retrieve the description value is based on this implementation:
public static String GetEnumDescription(Enum value)
{
String valueText = value.ToString();
Type type = value.GetType();
FieldInfo fi = type.GetField(valueText);
Object[] attributes = fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
DescriptionAttribute attribute = (DescriptionAttribute)attributes[0];
return attribute.Description;
}
return valueText;
}
You can find a working demo here.

Getting expression text

I want to pass the name of a property of a model to a method. Instead of using the name as string, I am using lambda expression as it is easy to make a typo, and also property names may be changed. Now if the property is a simple property (e.g: model.Name) I can get the name from the expression. But if it is a nested property (e.g: model.AnotherModel.Name) then how can I get full text ("AnotherModel.Name") from the expression. For example, I have the following classes:
public class BaseModel
{
public ChildModel Child { get; set; }
public List<ChildModel> ChildList { get; set; }
public BaseModel()
{
Child = new ChildModel();
ChildList = new List<ChildModel>();
}
}
public class ChildModel
{
public string Name { get;set; }
}
public void GetExpressionText<T>(Expression<Func<T, object>> expression)
{
string expText;
//what to do??
return expText;
}
GetExpressionText<BaseModel>(b => b.Child); //should return "Child"
GetExpressionText<BaseModel>(b => b.Child.Name); //should return "Child.Name"
GetExpressionText<BaseModel>(b => b.ChildList[0].Name); //should return "ChildList[0].Name"
My first thought was to use expression.Body.ToString() and tweak that a bit, but you would still need to deal with Unary (convert) etc. Assuming this is for logging and you want more control, the below can be used for formatting as wanted (e.g. if you want Child->Name for display purposes, string.Join("->",..) can be used). It may not be complete, but should you find any unsupported types, they should be easy to add.
PS: this post was generated before the question was closed. Just noticed it was reopend and submitting it now, but I haven't checked if particulars have been changed.
public string GetName(Expression e, out Expression parent)
{
if(e is MemberExpression m){ //property or field
parent = m.Expression;
return m.Member.Name;
}
else if(e is MethodCallExpression mc){
string args = string.Join(",", mc.Arguments.SelectMany(GetExpressionParts));
if(mc.Method.IsSpecialName){ //for indexers, not sure this is a safe check...
return $"{GetName(mc.Object, out parent)}[{args}]";
}
else{ //other method calls
parent = mc.Object;
return $"{mc.Method.Name}({args})";
}
}
else if(e is ConstantExpression c){ //constant value
parent = null;
return c.Value?.ToString() ?? "null";
}
else if(e is UnaryExpression u){ //convert
parent= u.Operand;
return null;
}
else{
parent =null;
return e.ToString();
}
}
public IEnumerable<string> GetExpressionParts(Expression e){
var list = new List<string>();
while(e!=null && !(e is ParameterExpression)){
var name = GetName(e,out e);
if(name!=null)list.Add(name);
}
list.Reverse();
return list;
}
public string GetExpressionText<T>(Expression<Func<T, object>> expression) => string.Join(".", GetExpressionParts(expression.Body));
You could use the C# 6.0 feature: nameof(b.Child) "Used to obtain the simple (unqualified) string name of a variable, type, or member."
which will also change on renaming. But this will only return the propertyname and not the complete path. Returning a complete path will be difficult, because only one instance is passed.
Closest i know right now is by simply using expression.Body.ToString() which would result in b.ChildList.get_Item(0).Name as a result.
You would still have to remove the first b. from the string if not wanted, and you could go even further to your intended output with Regex by replacing the get_Item(0) with the typical Index-Accessor.
(Also i had to make the ChildList and the Name-Property of ChildModel public to get it to work)
This Should get you most of the way there:
public static string GetFullPath<T>(Expression<Func<T>> action)
{
var removeBodyPath = new Regex(#"value\((.*)\).");
var result = action.Body.ToString();
var replaced = removeBodyPath.Replace(result, String.Empty);
var seperatedFiltered = replaced.Split('.').Skip(1).ToArray();
return string.Join(".", seperatedFiltered);
}
It gets ugly quite quickly...
public static string GetExpressionText<T>(Expression<Func<T, object>> expression)
{
bool needDot = false;
Expression exp = expression.Body;
string descr = string.Empty;
while (exp != null)
{
if (exp.NodeType == ExpressionType.MemberAccess)
{
// Property or field
var ma = (MemberExpression)exp;
descr = ma.Member.Name + (needDot ? "." : string.Empty) + descr;
exp = ma.Expression;
needDot = true;
}
else if (exp.NodeType == ExpressionType.ArrayIndex)
{
// Array indexer
var be = (BinaryExpression)exp;
descr = GetParameters(new ReadOnlyCollection<Expression>(new[] { be.Right })) + (needDot ? "." : string.Empty) + descr;
exp = be.Left;
needDot = false;
}
else if (exp.NodeType == ExpressionType.Index)
{
// Object indexer (not used by C#. See ExpressionType.Call)
var ie = (IndexExpression)exp;
descr = GetParameters(ie.Arguments) + (needDot ? "." : string.Empty) + descr;
exp = ie.Object;
needDot = false;
}
else if (exp.NodeType == ExpressionType.Parameter)
{
break;
}
else if (exp.NodeType == ExpressionType.Call)
{
var ca = (MethodCallExpression)exp;
if (ca.Method.IsSpecialName)
{
// Object indexer
bool isIndexer = ca.Method.DeclaringType.GetDefaultMembers().OfType<PropertyInfo>().Where(x => x.GetGetMethod() == ca.Method).Any();
if (!isIndexer)
{
throw new Exception();
}
}
else if (ca.Object.Type.IsArray && ca.Method.Name == "Get")
{
// Multidimensiona array indexer
}
else
{
throw new Exception();
}
descr = GetParameters(ca.Arguments) + (needDot ? "." : string.Empty) + descr;
exp = ca.Object;
needDot = false;
}
}
return descr;
}
private static string GetParameters(ReadOnlyCollection<Expression> exps)
{
var values = new string[exps.Count];
for (int i = 0; i < exps.Count; i++)
{
if (exps[i].NodeType != ExpressionType.Constant)
{
throw new Exception();
}
var ce = (ConstantExpression)exps[i];
// Quite wrong here... We should escape string values (\n written as \n and so on)
values[i] = ce.Value == null ? "null" :
ce.Type == typeof(string) ? "\"" + ce.Value + "\"" :
ce.Type == typeof(char) ? "'" + ce.Value + "\'" :
ce.Value.ToString();
}
return "[" + string.Join(", ", values) + "]";
}
The code is quite easy to read, but it is quite long... There are 4 main cases: MemberAccess, that is accessing a property/field, ArrayIndex that is using the indexer of a single-dimensional array, Index that is unused by the C# compiler, but that should be using the indexer of an object (like the [...] of the List<> you are using), and Call that is used by C# for using an indexer or for accessing multi-dimensional arrays (new int[5, 4]) (and for other method calls, but we disregard them).
I support multidimensional arrays, jagged array s(arrays of arrays, new int[5][]) or arrays of indexable objects (new List<int>[5]) or indexable objects of indexable objects (new List<List<int>>). There is even support for multi-property indexers (indexers that use more than one key value, like obj[1, 2]). Small problem: printing the "value" of the indexers: I support only null, integers of various types, chars and strings (but I don't escape them... ugly... if there is a \n then it won't be printed as \n). Other types are not really supported... They will print what they will print (see GetParameters() if you want)

Get source code representation of generic type?

I'm trying to get generic type in string I get something more like IL representation, I need the real source representation for emitting it.
Type t = typeof(Stack<string>);
string source = t.Name; //Stack`1[System.String]
string source = t.ToString(); //System.Collections.Generic.Stack`1[System.String]
I just need:
string source //Stack<string>
I've got this extension method, GetPrettyName(). This is basically it:
public static string GetPrettyName(this Type type)
{
var retval = type.Name;
if (type.IsGenericType)
{
var genargNames = type.GetGenericArguments().Select(t => GetPrettyName(t));
var idx = type.Name.IndexOf('`');
var typename = (idx > 0) ? type.Name.Substring(0, idx) : type.Name;
retval = String.Format("{0}.{1}<{2}>", type.Namespace, typename, String.Join(", ", genargNames));
}
else if (type.IsArray)
{
retval = GetPrettyName(type.GetElementType()) + "[]";
}
else if (String.IsNullOrEmpty(retval))
{
retval = type.Name;
}
return retval;
}
It operates recursively on each generic type parameter and builds out the full name in a format that's close to the code representation. It's good enough for our purposes (its just used in logging messages here). It can handle generics and arrays, but does not handle Entity Framework proxies that well.
Here's a version of Amy's answer with support for nested classes and multi-dimensional arrays.
public static string GetSourceCodeRepresentation(this Type type)
{
return GetSourceCodeRepresentationInt(type, new List<Type>());
}
private static string GetSourceCodeRepresentationInt(Type type, List<Type> travesed)
{
travesed.Add(type);
var prefixName = string.Empty;
if (type.DeclaringType != null)
{
if (!travesed.Contains(type.DeclaringType))
prefixName = GetSourceCodeRepresentationInt(type.DeclaringType, travesed) + ".";
}
else if (!string.IsNullOrEmpty(type.Namespace))
prefixName = type.Namespace + ".";
if (type.IsGenericType)
{
var genargNames = type.GetGenericArguments().Select(type1 => GetSourceCodeRepresentationInt(type1, new List<Type>()));
var idx = type.Name.IndexOf('`');
var typename = idx > 0 ? type.Name.Substring(0, idx) : type.Name;
return $"{prefixName}{typename}<{string.Join(", ", genargNames.ToArray())}>";
}
if (type.IsArray)
{
return $"{GetSourceCodeRepresentation(type.GetElementType())}[{new string(Enumerable.Repeat(',', type.GetArrayRank() - 1).ToArray())}]";
}
return $"{prefixName}{type.Name}";
}

C# Iterating through an enum? (Indexing a System.Array)

I have the following code:
// Obtain the string names of all the elements within myEnum
String[] names = Enum.GetNames( typeof( myEnum ) );
// Obtain the values of all the elements within myEnum
Array values = Enum.GetValues( typeof( myEnum ) );
// Print the names and values to file
for ( int i = 0; i < names.Length; i++ )
{
print( names[i], values[i] );
}
However, I cannot index values. Is there an easier way to do this?
Or have I missed something entirely!
Array values = Enum.GetValues(typeof(myEnum));
foreach( MyEnum val in values )
{
Console.WriteLine (String.Format("{0}: {1}", Enum.GetName(typeof(MyEnum), val), val));
}
Or, you can cast the System.Array that is returned:
string[] names = Enum.GetNames(typeof(MyEnum));
MyEnum[] values = (MyEnum[])Enum.GetValues(typeof(MyEnum));
for( int i = 0; i < names.Length; i++ )
{
print(names[i], values[i]);
}
But, can you be sure that GetValues returns the values in the same order as GetNames returns the names ?
You need to cast the array - the returned array is actually of the requested type, i.e. myEnum[] if you ask for typeof(myEnum):
myEnum[] values = (myEnum[]) Enum.GetValues(typeof(myEnum));
Then values[0] etc
You can cast that Array to different types of Arrays:
myEnum[] values = (myEnum[])Enum.GetValues(typeof(myEnum));
or if you want the integer values:
int[] values = (int[])Enum.GetValues(typeof(myEnum));
You can iterate those casted arrays of course :)
How about a dictionary list?
Dictionary<string, int> list = new Dictionary<string, int>();
foreach( var item in Enum.GetNames(typeof(MyEnum)) )
{
list.Add(item, (int)Enum.Parse(typeof(MyEnum), item));
}
and of course you can change the dictionary value type to whatever your enum values are.
What about using a foreach loop, maybe you could work with that?
int i = 0;
foreach (var o in values)
{
print(names[i], o);
i++;
}
something like that perhaps?
Here is another. We had a need to provide friendly names for our EnumValues. We used the System.ComponentModel.DescriptionAttribute to show a custom string value for each enum value.
public static class StaticClass
{
public static string GetEnumDescription(Enum currentEnum)
{
string description = String.Empty;
DescriptionAttribute da;
FieldInfo fi = currentEnum.GetType().
GetField(currentEnum.ToString());
da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi,
typeof(DescriptionAttribute));
if (da != null)
description = da.Description;
else
description = currentEnum.ToString();
return description;
}
public static List<string> GetEnumFormattedNames<TEnum>()
{
var enumType = typeof(TEnum);
if (enumType == typeof(Enum))
throw new ArgumentException("typeof(TEnum) == System.Enum", "TEnum");
if (!(enumType.IsEnum))
throw new ArgumentException(String.Format("typeof({0}).IsEnum == false", enumType), "TEnum");
List<string> formattedNames = new List<string>();
var list = Enum.GetValues(enumType).OfType<TEnum>().ToList<TEnum>();
foreach (TEnum item in list)
{
formattedNames.Add(GetEnumDescription(item as Enum));
}
return formattedNames;
}
}
In Use
public enum TestEnum
{
[Description("Something 1")]
Dr = 0,
[Description("Something 2")]
Mr = 1
}
static void Main(string[] args)
{
var vals = StaticClass.GetEnumFormattedNames<TestEnum>();
}
This will end returning "Something 1", "Something 2"
Another solution, with interesting possibilities:
enum Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
static class Helpers
{
public static IEnumerable<Days> AllDays(Days First)
{
if (First == Days.Monday)
{
yield return Days.Monday;
yield return Days.Tuesday;
yield return Days.Wednesday;
yield return Days.Thursday;
yield return Days.Friday;
yield return Days.Saturday;
yield return Days.Sunday;
}
if (First == Days.Saturday)
{
yield return Days.Saturday;
yield return Days.Sunday;
yield return Days.Monday;
yield return Days.Tuesday;
yield return Days.Wednesday;
yield return Days.Thursday;
yield return Days.Friday;
}
}
Old question, but a slightly cleaner approach using LINQ's .Cast<>()
var values = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();
foreach(var val in values)
{
Console.WriteLine("Member: {0}",val.ToString());
}
In the Enum.GetValues results, casting to int produces the numeric value. Using ToString() produces the friendly name. No other calls to Enum.GetName are needed.
public enum MyEnum
{
FirstWord,
SecondWord,
Another = 5
};
// later in some method
StringBuilder sb = new StringBuilder();
foreach (var val in Enum.GetValues(typeof(MyEnum))) {
int numberValue = (int)val;
string friendyName = val.ToString();
sb.Append("Enum number " + numberValue + " has the name " + friendyName + "\n");
}
File.WriteAllText(#"C:\temp\myfile.txt", sb.ToString());
// Produces the output file contents:
/*
Enum number 0 has the name FirstWord
Enum number 1 has the name SecondWord
Enum number 5 has the name Another
*/
Array has a GetValue(Int32) method which you can use to retrieve the value at a specified index.
Array.GetValue
You can simplify this using format strings. I use the following snippet in usage messages:
writer.WriteLine("Exit codes are a combination of the following:");
foreach (ExitCodes value in Enum.GetValues(typeof(ExitCodes)))
{
writer.WriteLine(" {0,4:D}: {0:G}", value);
}
The D format specifier formats the enum value as a decimal. There's also an X specifier that gives hexadecimal output.
The G specifier formats an enum as a string. If the Flags attribute is applied to the enum then combined values are supported as well. There's an F specifier that acts as if Flags is always present.
See Enum.Format().
Here is a simple way to iterate through your custom Enum object
For Each enumValue As Integer In [Enum].GetValues(GetType(MyEnum))
Print([Enum].GetName(GetType(MyEnum), enumValue).ToString)
Next
Ancient question, but 3Dave's answer supplied the easiest approach. I needed a little helper method to generate a Sql script to decode an enum value in the database for debugging. It worked great:
public static string EnumToCheater<T>() {
var sql = "";
foreach (var enumValue in Enum.GetValues(typeof(T)))
sql += $#"when {(int) enumValue} then '{enumValue}' ";
return $#"case ?? {sql}else '??' end,";
}
I have it in a static method, so usage is:
var cheater = MyStaticClass.EnumToCheater<MyEnum>()

Categories