Search code examples
c#selectlistitem

How to get constants from a static class into a List<SelectListItem>


I have a static class with constants that I want to store in a list to be used in a DropDown

public static class DateFormatConstant
{
    [Display(Name = "DD/MM/YYYY")]
    public const string DayMonthYear = "dd/MM/yyyy";
    [Display(Name = "MM/DD/YYYY")]
    public const string MonthDayYear = "MM/dd/yyyy";
    [Display(Name = "YYYY/MM/DD")]
    public const string YearMonthDay = "yyyy/MM/dd";
    [Display(Name = "MM/DD/YY")]
    public const string MonthDayTwoDigitYear = "MM/dd/yy";
}

Is there a way to get them into a List<SelectListItem>?


Solution

  • The answer from @maembe utilizing Reflection is a good way to go. But since you already are decorating your constants with the Display attribute I'll expand this solution to use the attribute. Note that I use LINQ's Query Syntax here as opposed to the Method Syntax in the other answer.

    Here's my code:

    var selectLisItems =
        from f in typeof(DateFormatConstant).GetFields(BindingFlags.Public | BindingFlags.Static)
        where f.IsLiteral && f.FieldType == typeof(string)
        from a in f.CustomAttributes
        where a.AttributeType == typeof(DisplayAttribute)
        let na = a.NamedArguments.First(x => x.MemberName == nameof(DisplayAttribute.Name))
        select new SelectListItem
        {
            Text = (string)na.TypedValue.Value,
            Value = (string)f.GetRawConstantValue()
        };
    
    var list = selectLisItems.ToList();
    

    What does it actually do? Let's look at the parts of the query.

        from f in typeof(DateFormatConstant).GetFields(BindingFlags.Public | BindingFlags.Static)
        where f.IsLiteral && f.FieldType == typeof(string)
    

    Here I'm selecting all constants from the DateFormatConstant class that are of type string.

        from a in f.CustomAttributes
        where a.AttributeType == typeof(DisplayAttribute)
    

    Now I'm limiting to constants that actually have the Display attribute. Note that the "real" type here is DisplayAttribute

        let na = a.NamedArguments.First(x => x.MemberName == nameof(DisplayAttribute.Name))
    

    Next is going through the arguments of the attribute and search for Name. I can safely use First here because I already limited to the DisplayAttribute type and know therefore that it has a Name property.

        select new SelectListItem
        {
            Text = (string)na.TypedValue.Value,
            Value = (string)f.GetRawConstantValue()
        };
    
    var list = selectLisItems.ToList();
    

    At last I'm constructing SelectListItem from the attribute and the fields and create the desired List<SelectListItem> from the query.

    Note: All this assumes that you want all string constants from your class to be contained in the list. If your class contains more constants that should go in different lists you can use the GroupName property of the Display attribute to group constants together. I'll leave it as an excercise to expand the code accordingly.