Reputation: 2727
We have the following enum:
public enum TrafficLight
{
[Description("../../images/dot-red.png")]
[AwesomeIcon("<i class='fa fa-lg fa-circle v4-red'></i>")]
Red = 0,
[Description("../../images/dot-yellow.png")]
[AwesomeIcon("<i class='fa fa-lg fa-circle v4-yellow'></i>")]
Yellow = 1,
[Description("../../images/dot-green.png")]
[AwesomeIcon("<i class='fa fa-lg fa-circle v4-green'></i>")]
Green = 2,
[Description("../../images/dot-gold.png")]
[AwesomeIcon("<i class='fa fa-lg fa-circle v4-gold'></i>")]
Gold = 3,
[Description("../../images/dot-grey.png")]
[AwesomeIcon("<i class='fa fa-lg fa-circle v4-grey'></i>")]
Grey = 99
}
AwesomeIcon
inherits from the DescriptionAttribute
class:
public class AwesomeIcon : DescriptionAttribute
{
public AwesomeIcon(string icon)
: base(icon)
{
}
}
The problem is when the Enum description is accessed more than once, the order of the attributes changes. For example, we're getting the description like so:
public static string GetEnumDescription(Enum value, int index = 0)
{
var fi = value.GetType().GetField(value.ToString());
var attributes =
fi.GetCustomAttributes(typeof (DescriptionAttribute), false) as DescriptionAttribute[];
if(attributes == null)
{
return string.Empty;
}
if (attributes.Any() && index <= attributes.Length)
{
return attributes[index].Description;
}
return value.ToString();
}
First Access
GetEnumDescription(TrafficLight.Red);
//Returns "../../images/dot-red.png" as expected.
//Debug GetEnumDescription attributes variable
[0]Description
[1]AwesomeIcon
Second Access
GetEnumDescription(TrafficLight.Yellow);
//Returns "<i class='fa fa-lg fa-circle v4-yellow'></i>"
// which is the value of AwesomeIcon.
//Debug GetEnumDescription attributes variable
[0]AwesomeIcon
[1]Description
Every access after the order remains AwesomeIcon
, then Description
.
The problem is that AwesomeIcon inherits from Description and it's getting picked up. If the order remains the same in the array it wouldn't be a problem, I can reference it by index.
I've not experienced this before, any ideas?
Upvotes: 0
Views: 78
Reputation: 14677
As a quick fix you can sort the attributes by Names, this will help you getting them in the same order every time.
var attributes =
fi.GetCustomAttributes(typeof (DescriptionAttribute), false) as object[];
if(attributes.Any())
{
// Ascending order
Array.Sort(attributes, (x, y) =>
String.Compare(x.GetType().Name, y.GetType().Name));
}
And fix a bug in GetEnumDescription(Enum value, int index = 0)
:
if (attributes.Any() && index <= attributes.Length)
should be
if (attributes.Any() && index < attributes.Length)
Upvotes: 1
Reputation: 28272
You can make sure you have a DescriptionAttribute
(even if it's inherited), with something like:
var fi = value.GetType().GetField(value.ToString());
var attributes = fi.GetCustomAttributes(typeof (DescriptionAttribute), false);
var myDescriptionAttribute =
attributes.FirstOrDefault(x => x.GetType() == typeof(DescriptionAttribute)) as DescriptionAttribute;
And forget about indices. Attributes are not guaranteed an order so an index is rather useless.
By using x => x.GetType() == typeof(DescriptionAttribute)
you get an exact type match.
I've made a fiddle demonstrating two possible methods (using generics or just finding the DescriptionAttribute
): https://dotnetfiddle.net/StUvqf
But basically:
public static string GetEnumDescription(Enum value)
{
var fi = value.GetType().GetField(value.ToString());
var attributes = fi.GetCustomAttributes(typeof (DescriptionAttribute), false);
var theDescriptionAttribute = attributes.FirstOrDefault(x => x.GetType() == typeof (DescriptionAttribute)) as DescriptionAttribute;
if (theDescriptionAttribute == null)
{
return string.Empty;
}
return theDescriptionAttribute.Description;
}
or:
public static string GetEnumDescription<T>(Enum value)
where T : DescriptionAttribute
{
var fi = value.GetType().GetField(value.ToString());
var attributes = fi.GetCustomAttributes(typeof (T), false);
var theDescriptionAttribute = attributes.FirstOrDefault(x => x.GetType() == typeof (T)) as T;
if (theDescriptionAttribute == null)
{
return string.Empty;
}
return theDescriptionAttribute.Description;
}
Of course might need some more null
checkings, but I'll leave that to you
As an extra hint, FieldInfo.GetCustomAttributes
never returns null (it may return an empty array, but not null), so you could just remove that null check :-)
Upvotes: 1