FirstByte
FirstByte

Reputation: 631

How do I get the default value of an Enum using a non-generic Type?

I have a method that iterates through an object's properties and sets the values to their type's default values. Some properties are enums. I have another function that gets the default value of the enum (not 0), but it requires passing the enum type which is not known in the current method.

    [DefaultValue(Red)]
    public enum Colors
    {
        Red = 1,
        green = 2
    }

    // In another class
    public static TEnum GetDefaultValue<TEnum>() where TEnum : struct
    {
        Type t = typeof(TEnum);
        DefaultValueAttribute[] attributes = (DefaultValueAttribute[])t.GetCustomAttributes(typeof(DefaultValueAttribute), false);
        if (attributes != null && attributes.Length > 0)
        {
        return (TEnum)attributes[0].Value;
        }
        else
        {
        return default(TEnum);
        }
    }


    public static void ClearObject<T>(object obj)
    {
        obj = (T)obj;

        PropertyInfo[] props = obj.GetType().GetProperties();

        string propName = "";

        try
        {
        foreach (PropertyInfo pi in props)
        {
            propName = pi.Name;

            Type t = Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType;
            if (t.IsEnum)
            {
            //  This works
            //  var val = EnumFunctions.GetDefaultValue<Colors>();

            // The error is here
            var val = EnumFunctions.GetDefaultValue<t>();
            //                                     ^^^

            pi.SetValue(obj, val);
            }
            // In case of nullable value types int,datetime, etc - set null  
            else if (Nullable.GetUnderlyingType(pi.PropertyType) != null)
            pi.SetValue(obj, null);
            else
            pi.SetValue(obj, null, null);
        }
        }
        catch (Exception e)
        {
        string msg = $"Error for {propName}: {e.Message}";
        throw new Exception(msg);
        }
    }

I've tried typeof(t), t.GetType().

I want the default value for a Colors enum property to be Red. The line causing the error is

var val = EnumFunctions.GetDefaultValue<t>();
Error   CS0118  't' is a variable but is used like a type

Upvotes: 0

Views: 1116

Answers (3)

FirstByte
FirstByte

Reputation: 631

Per my comment above (thought I could post code). I also wanted to ensure that enums were instantiated to a valid value. Some enums do not have a 0 so the default of numeric types wouldn't work.

public MyClass() 
{
    // Sets default property values for all but dates
    Basefunctions.Clear<InternalExaminer>(this);

    // Sets default values by [DefalutValue()] tag.
    foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(this))
    {
        pd.ResetValue(this);
    }

    // Sets date properties to current date.
    Basefunctions.SetDates<MyClass>(this);
}

public static class Basefunctions
{
    public static void SetDates<T>(object obj)
    {
        string propName = "";

        try
        {
            obj = (T)obj;

            PropertyInfo[] props = obj.GetType().GetProperties()
                           .Where(p => p.PropertyType == typeof(DateTime)).ToArray();
            if (props != null)
            {
                DateTime date = DateTime.Now;
                foreach (PropertyInfo pi in props)
                {
                    propName = pi.Name;
                    if (Nullable.GetUnderlyingType(pi.PropertyType) == null)
                        pi.SetValue(obj, date);
                }
            }
        }
        catch (Exception e)
        {
            throw new Exception($"Could not set date for {propName}.\n{e.Message}");
        }
    }

    public static void Clear<T>(object obj)
    {
        obj = (T)obj;

        PropertyInfo[] props = obj.GetType().GetProperties();

        string propName = "";

        try
        {
            foreach (PropertyInfo pi in props)
            {
                propName = pi.Name;

                Type t = Nullable.GetUnderlyingType(pi.PropertyType) ?? pi.PropertyType;

                if (Nullable.GetUnderlyingType(pi.PropertyType) != null)
                    pi.SetValue(obj, null);
                else
                {
                    var val = GetDefaultVal(t);
                    if (t.IsEnum)
                    {
                        // In case enum does not have a 0
                        if (!Enum.IsDefined(t, val))
                            val = EnumMin(pi.PropertyType);
                    }
                    pi.SetValue(obj, val);
                }
            }
        }
        catch (Exception e)
        {
            string msg = $"Error for {propName}: {e.Message}";
            throw new Exception(msg);
        }
    }

    private static object GetDefaultVal(Type type)
    {
        DefaultValueAttribute att = (DefaultValueAttribute)type.GetCustomAttribute(typeof(DefaultValueAttribute));

        if (att != null)
            return att.Value;
        else
            return type.IsValueType ? Activator.CreateInstance(type) : null;
    }

    private static object EnumMin(Type t)
    {
        Array x = Enum.GetValues(t);
        var ret = x.GetValue(0);
        return ret;
    }

    private static object EnumMax(Type t)
    {
        Array x = Enum.GetValues(t);
        var ret = x.GetValue(x.Length - 1);
        return ret;
    }
}

Upvotes: 0

Sohaib Jundi
Sohaib Jundi

Reputation: 1664

You can also invoke the method with reflection like this:

typeof(EnumFunctions).GetMethod("GetDefaultValue").MakeGenericMethod(t).Invoke(null, null);

Upvotes: 0

Oleh Nechytailo
Oleh Nechytailo

Reputation: 2195

You don't need generics here

public static object GetDefaultValue(Type type)
{
    DefaultValueAttribute[] attributes = (DefaultValueAttribute[])type.GetCustomAttributes(typeof(DefaultValueAttribute), false);
    if (attributes != null && attributes.Length > 0)
    {
        return attributes[0].Value;
    }
    else
    {
        return null;
    }
}

And then you use it like this

 var val = EnumFunctions.GetDefaultValue(t);
 if(val != null)
     pi.SetValue(obj, val);

Source of your confusion:

Generally speaking, generics are not runtime construct, they're compile-time construct, so you can't use them in reflection, because reflection works at run time.

Upvotes: 2

Related Questions