0

What I have:

I have a class where many of the properties use a specific type of attribute. This class also contains a property which is a nullable enum, and each value of the enum may also be decorated with the same attribute. For example:

// My basic class
public class Upgrade
{
    [Abbreviation("name")]
    public int string Name { get; set; }

    [Abbreviation("opt")]
    public bool Optional { get; set; } = true;

    [Abbreviation("type")]
    public UpgradeType? TypeOfUpgrade { get; set; } = null;
}

// My basic enum
public enum UpgradeType
{
    [Abbreviation("fl")]
    Full, 

    [Abbreviation("prt")]
    Partial
}

// My basic attribute
public class AbbreviationAttribute: Attribute 
{
    public string Value{ get; private set; }

    public AbbreviationAttribute(string value)
    {
        Value = value;
    }
}

What I'm trying to do:

I'm trying to get the name of each property and it's value. However, if the property is decorated with the AbbreviationAttribute, the value from the attribute should be used instead. Likewise, if the property is any type of enum, the value should be taken from the enum's AbbreviationAttribute, if it has one.

So for an object such as this:

Upgrade upgrade = new Upgrade 
{
    Name = "First Upgrade",
    Optional = false,
    TypeOfUpgrade = UpgradeType.Full
};

I'd like to output the string:

name=First Upgrade;opt=false;type=fl

How can I achieve this?

What I've tried:

I've can get the attribute from the property, however I'm not sure how to access it from the enum property. So in the above example, I can get the AbbreviationAttribute from the TypeOfUpgrade property, but since it's an enum, I need to get the AbbreviationAttribute from it's value.

string values = string.Empty;
// foreach property
foreach(PropertyInfo pi in this.GetType().GetProperties())
{
    // get the attribute from the property, if it exists
    AbbreviationAttribute attr = pi.GetCustomAttribute<AbbreviationAttribute>();

    if (attr != null)
    {
        //append the value from the attribute, instead of the property name
        values += $"{attr.Value}=";

        // if property is an enum (nullable or otherwise)
        if (pi.PropertyType.IsEnum || pi.PropertyType.IsNullableEnum())
        {
            // get the type of enum
            Type type = Nullable.GetUnderlyingType(pi.PropertyType);
            if (type == null)
            {
                type = pi.PropertyType;
            }

            // --------------------------
            // Now I have the type of enum, and the value from the PropertyInfo,
            // how can I access the AbbreviationAttribute that's on the enum's value? 

        }
    }
}

// helper method
public static bool IsNullableEnum(this Type t)
{
    Type u = Nullable.GetUnderlyingType(t);
    return (u != null) && u.IsEnum;
}
devklick
  • 2,000
  • 3
  • 30
  • 47

1 Answers1

0

An enum value is a static field of the enum type.

So once you have an enum value, you get the Type for the enum type, get the field info for the enum value, and get the attribute from the member info.

public static TAttributeType GetEnumValueAttribute<TAttributeType>(Enum val) 
    where TAttributeType : Attribute
{
    if (val == null)
    {
        return null;
    }

    return 
        val.GetType().
        GetMember(val.ToString())?.
        FirstOrDefault()?.
        GetCustomAttribute<TAttributeType>();
}

And in your loop:

foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
    // get the attribute from the property, if it exists
    AbbreviationAttribute attr = pi.GetCustomAttribute<AbbreviationAttribute>();

    if (attr != null)
    {
        //append the value from the attribute, instead of the property name
        values += $"{attr.Value}=";

        // if property is an enum (nullable or otherwise)
        if (pi.PropertyType.IsEnum || pi.PropertyType.IsNullableEnum())
        {
            values += $"{GetEnumValueAttribute<AbbreviationAttribute>((Enum)pi.GetValue(obj))?.Value};";
        }
        else
        {
            values += $"{pi.GetValue(obj)};";
        }
    }
}

You're really asking a duplicate of this question. I copied his code, but modernized it a bit.

  • 1
    Fantastic, thanks! I'd actually stumbled upon the question you've linked, but was struggling to apply it to my scenario, and wasnt sure if it was even possible. – devklick Jul 01 '19 at 19:51