Question

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.

Was it helpful?

Solution

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;
    }
}

OTHER TIPS

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.

Maybe I'm missing something, but why not just use your KnownSetting<T> class as is, and make new references to the same enum instances from a new class? Something like:

public static class KnownSettings {
  public readonly static KnownSetting<string> Name = KnownSetting<string>.Name;
  public readonly static KnownSetting<int> Size = KnownSetting<int>.Size;
  // etc.
}

Then you can use the values as desired:

var x = KnownSettings.Name;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top