使用泛型实现类型安全的枚举模式

10
如何在通用类上实现类型安全的枚举模式?假设它是按以下方式实现的。
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;
    }
}

这种实现方式是正确的,因为构造函数仍然是私有的,但是当使用这种构造时,看起来不太对:

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

然后,一种选择是将其分成两个部分,即 KnownSetting 容器类和 Setting 实现,但是构造函数的范围不能是私有的,以便从容器内部实例化。
如何实现此模式,使其泛型方面对最终用户保密,但仍具有强类型?是否有更合适的模式,或者是否有更好的实现方式?
更新:我添加了第二个示例,以说明我确实希望设置的类型是通用的。

4
你的静态字段对于每个T都有单独的值。你不想要这样。 - SLaks
@SLaks 为什么我不想要那个呢? - Spooles
1
@Spooles,除非您认为 KnownSetting<int>.Name != KnownSetting<String>.Name 没问题,否则您不会想要那个。 - Mike Zboray
@mikez 你说得对,当然我不想要那个。这就是我请求的重点。 - Spooles
顺便说一下:我会使用“string”而不是“String”。只是一个小细节... - atlaste
显示剩余5条评论
3个回答

4
在基类型中创建一个帮助方法,使用另一个类型并创建一个已知的设置类。您需要创建Create方法,因为基础构造函数是Setting(string, object, Func)。这也是我引入另一个泛型变量(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;
    }
}

这对什么有帮助?Predefined有一个私有构造函数,但未使用。此处设置缺少通用和私有构造函数,这是问题的核心。 - Spooles
你是对的,我误解了问题。我改变了我的解决方案以反映这个问题;这应该是他正在寻找的答案。 - atlaste
如果您拥有受保护的Create方法,为什么不将ctor设为私有呢? - Spooles
这与类型有关。构造函数的类型为Setting<T>([...]),因此您无法调用Setting<not-T>的构造函数(仅仅因为那不是您的基类)。在我的代码中,通过调用Setting<T>.Create<U>来解决这个问题 - 它位于基类中,因此可以调用Setting<U>(其位于同一范围内)。您需要继承才能使其工作 - 因此您需要某种受保护的构造函数。希望它有些道理...只需尝试它 - 它不会编译 :-) - atlaste

3
在一个新的类中声明静态数据:
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);
}

在C#中,它可以使用相同的名称,因为类名在名称加上泛型类型参数计数时是唯一的。

1
此外,您可以让 KnownSetting<T> 继承自 KnownSetting,这样就有了一个“通用”的非类型化形式,您可以根据需要将它们分组在一起。 - Bobson
但是,KnownSetting<T> 的构造函数需要公开才能使其正常工作。 - default.kramer
@usr 还不错,虽不完美但在我看来是迄今为止最好的想法。 - Spooles
@usr 从我的解决方案中可以看出,它也可以被保护起来,从而得到适当的隐藏。请注意,您不能直接使用受保护的构造函数;您是正确的,它必须是内部的,但说实话,这总是让我感到不舒服... - atlaste
@StefandeBruijn 在我的代码中,当我调用内部函数时,我会非常小心。我总是问自己:我应该调用这个吗?对于公共函数,我总是假设我可以调用它们而不违反架构。这个约定为我解决了问题。 - usr
@usr 我也是这样,这就是为什么我宁愿避免使用 internal,除非我真的、真的需要它们。这就像 C++ 中的 friend 类一样 - 你知道你将需要它们,但你宁愿不用。在这种情况下,可以避免使用,所以我宁愿不用。 - atlaste

0
也许我漏掉了什么,但为什么不直接使用您的KnownSetting<T>类,并从新类中引用相同的枚举实例呢?像这样:
public static class KnownSettings {
  public readonly static KnownSetting<string> Name = KnownSetting<string>.Name;
  public readonly static KnownSetting<int> Size = KnownSetting<int>.Size;
  // etc.
}

然后您可以根据需要使用这些值:

var x = KnownSettings.Name;

因为 KnownSetting<T> 和 KnownSetting(没有泛型)不是同一个类,所以非泛型的无法看到私有构造函数或泛型的构造函数。 - Spooles
@Spooles:但它不需要看到构造函数,它只需要看到已构建的public readonly static字段。所有KnownSetting<T>值都可以完全在KnownSetting<T>内部构建,但客户端仍然可以通过另一个类上的另一个字段访问这些值。 - Daniel Pryden

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接