Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type safe enum pattern implementation with generics

How could the typesafe enum pattern be implemented on a generic class? Let's assume it's implemented along these lines

public class KnownSetting<T>
{
    public readonly static KnownSetting<String> Name = new KnownSetting<String>("name", "Default Name", t => t);
    public readonly static KnownSetting<int> Size = new KnownSetting<String>("size", "25", t => Converter.ToInt32);

    public String Key { get; set; }
    public T DefaultValue { get; set; }
    public Func<String, T> Converter { get; set; }

    private KnownSetting(String key, T defaultValue, Func<String, T> converter)
    {
        Key = key;
        DefaultValue = defaultValue;
        Converter = converter;
    }
}

The implementation of the pattern is correct this way since the constructor remains private, but when using this construct, it looks wrong:

public static class Program
{
    public static void main()
    {
        var x = KnownSetting<?>.Name;
    }
}

Then an option would be to split it in two, KnownSetting container class and the Setting implementation, but then the scope of the constructor cannot be private so as to be instantiated from within the container.

How can this pattern be implemented so that the generics aspect of it stays concealed from the end-user, but remains strongly typed? Is there a more suitable pattern, or is there a better way to implement it?

Update I added a second sample to illustrate that I do want the type of the setting to be generic.

like image 366
Spooles Avatar asked Jan 17 '13 16:01

Spooles


2 Answers

Declare the static data in a new class:

public class KnownSetting
{
    public readonly static KnownSetting<String> Name = new KnownSetting<String>("name", "Default Name", t => t);
    public readonly static KnownSetting<int> Size = new KnownSetting<String>("size", "25", t => Converter.ToInt32);
}

It can have the same name in C# because class names are unique on name plus generic type argument count.

like image 27
usr Avatar answered Nov 27 '22 11:11

usr


Create a helper method in the base type that uses another type and create a known settings class. You need the Create method because the base constructor is Setting(string, object, Func). This is also why I introduce another generic variable (U):

public class KnownSetting : Setting<object>
{
    private KnownSetting(string key, object defaultValue, Func<string, object> converter) : base(key, defaultValue, converter) { }

    public readonly static Setting<string> Name = Create<string>("name", "Default Name", t => t);
    public readonly static Setting<int> Size = Create<int>("size", 25, t => Convert.ToInt32(t));
}

public class Setting<T>
{
    public string Key { get; set; }
    public T DefaultValue { get; set; }
    public Func<string, T> Converter { get; set; }

    protected static Setting<U> Create<U>(string key, U defaultValue, Func<string, U> converter)
    {
        return new Setting<U>(key, defaultValue, converter);
    }

    protected Setting(string key, T defaultValue, Func<string, T> converter)
    {
        Key = key;
        DefaultValue = defaultValue;
        Converter = converter;
    }
}
public static class Program
{
    static void Main(string[] args)
    {
        var x = KnownSetting.Name;
    }
}
like image 139
atlaste Avatar answered Nov 27 '22 10:11

atlaste