You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从父类容器中获取配置子类的不同原始类型值?

配置元素类型提取解决方案

我想创建一组类来存储配置文件中的各种原始类型配置值(布尔、字符串、整数等)。虽然知道这有点重复造轮子,但这些类除了读取配置文件外,还集成了ILogger日志功能,并且在未加载配置文件时支持通过用户输入获取值。

目前遇到的问题是:将这些对象存储在ConfigElement类型的字典中后,无法直接提取各自的原始类型值。我清楚用同一个方法返回不同类型会破坏多态性,希望找到可行的替代方案。

现有代码示例

abstract class ConfigElement
{
    // 所有ConfigElement共享的成员
    public static ILogger logger;
    public static bool useConfig;

    protected string _key;
    protected string _commandPrompt;

    protected ConfigElement(string key, string commandPrompt)
    {
        _key = key;
        _commandPrompt = commandPrompt;
    }

    abstract public void SetValue(string value);

    public static ConfigElement Create(string key, string commandPrompt, string inValue)
    {
        return new ConfigStringElement(key, commandPrompt, inValue);
    }

    public static ConfigElement Create(string key, string commandPrompt, bool inValue)
    {
        return new ConfigBoolElement(key, commandPrompt, inValue);
    }

}

class ConfigStringElement : ConfigElement
{
    public string Value;

    public ConfigStringElement(string key, string commandPrompt, string inValue = "") : base(key, commandPrompt)
    {
        this.Value = inValue;
    }

    override public void SetValue(string input)
    {
        Value = input;
    }

    public string GetValue()
    {
        if (!ConfigElement.useConfig)
        {
            Console.WriteLine(_commandPrompt);
            string userInput = Console.ReadLine();
            Value = userInput;
        }
        logger.Information($"Config Element: {_key} value: {Value}.");
        return Value;
    }

    override public string GetConfigString()
    {
        return Value;
    }
}

class ConfigBoolElement : ConfigElement
{
    public bool Value;

    public ConfigBoolElement(string key, string commandPrompt, bool inValue = false) : base(key, commandPrompt)
    {
        Value = inValue;
    }

    override public void SetValue(string input)
    {
        List<string> trueEntries = new List<string> { "yes", "y", "true", "t" };
        Value = trueEntries.Contains(input.ToLower());
    }

    public bool GetValue()
    {
        if (!ConfigElement.useConfig)
        {
            Value = GetTrueOrFalse(_commandPrompt);
        }
        logger.Information($"Config Element: {_key} value: {Value}.");

        return Value;
    }
}

可行解决方案

方案1:泛型抽象基类

将基类改为泛型类型,让每个子类对应具体的原始类型,既保留多态性,又能强类型获取值:

public interface IConfigElement
{
    string Key { get; }
    string CommandPrompt { get; }
    void SetValue(string value);
    string GetConfigString();
}

public abstract class ConfigElement<T> : IConfigElement
{
    public static ILogger Logger;
    public static bool UseConfig;

    public string Key { get; }
    public string CommandPrompt { get; }
    public T Value { get; protected set; }

    protected ConfigElement(string key, string commandPrompt, T defaultValue)
    {
        Key = key;
        CommandPrompt = commandPrompt;
        Value = defaultValue;
    }

    public abstract void SetValue(string value);
    public abstract string GetConfigString();

    public T GetValue()
    {
        if (!UseConfig)
        {
            Value = GetUserInput();
        }
        Logger.Information($"Config Element: {Key} value: {Value}.");
        return Value;
    }

    protected abstract T GetUserInput();
}

public class ConfigStringElement : ConfigElement<string>
{
    public ConfigStringElement(string key, string commandPrompt, string defaultValue = "") 
        : base(key, commandPrompt, defaultValue) { }

    public override void SetValue(string input) => Value = input;
    public override string GetConfigString() => Value;

    protected override string GetUserInput()
    {
        Console.WriteLine(CommandPrompt);
        return Console.ReadLine();
    }
}

public class ConfigBoolElement : ConfigElement<bool>
{
    private static readonly List<string> TrueValues = new List<string> { "yes", "y", "true", "t" };

    public ConfigBoolElement(string key, string commandPrompt, bool defaultValue = false) 
        : base(key, commandPrompt, defaultValue) { }

    public override void SetValue(string input) => Value = TrueValues.Contains(input.ToLower());
    public override string GetConfigString() => Value.ToString();

    protected override bool GetUserInput()
    {
        while (true)
        {
            Console.WriteLine(CommandPrompt);
            var input = Console.ReadLine()?.ToLower();
            if (TrueValues.Contains(input)) return true;
            if (new List<string> { "no", "n", "false", "f" }.Contains(input)) return false;
            Console.WriteLine("无效输入,请输入yes/no/y/n/true/false/t/f");
        }
    }
}

使用方式:
字典可以存储IConfigElement接口,获取值时强转为对应泛型类:

var configDict = new Dictionary<string, IConfigElement>
{
    {"UserName", new ConfigStringElement("UserName", "请输入用户名:")},
    {"IsEnabled", new ConfigBoolElement("IsEnabled", "是否启用功能?(yes/no):")}
};

var userName = ((ConfigElement<string>)configDict["UserName"]).GetValue();
var isEnabled = ((ConfigElement<bool>)configDict["IsEnabled"]).GetValue();

方案2:添加泛型TryGetValue方法

在基类中定义泛型的TryGetValue抽象方法,子类实现类型匹配逻辑,避免强制转换:

abstract class ConfigElement
{
    // 原有成员...

    public abstract bool TryGetValue<T>(out T value);
}

class ConfigStringElement : ConfigElement
{
    // 原有成员...

    public override bool TryGetValue<T>(out T value)
    {
        if (typeof(T) == typeof(string))
        {
            value = (T)(object)Value;
            return true;
        }
        value = default;
        return false;
    }
}

class ConfigBoolElement : ConfigElement
{
    // 原有成员...

    public override bool TryGetValue<T>(out T value)
    {
        if (typeof(T) == typeof(bool))
        {
            value = (T)(object)Value;
            return true;
        }
        value = default;
        return false;
    }
}

使用方式:

if (configDict["UserName"].TryGetValue(out string userName))
{
    // 使用userName
}

if (configDict["IsEnabled"].TryGetValue(out bool isEnabled))
{
    // 使用isEnabled
}

方案3:访问者模式

通过访问者模式,让不同类型的配置元素接受访问者处理,适合需要对多种类型执行复杂操作的场景:

public interface IConfigElementVisitor
{
    void Visit(ConfigStringElement element);
    void Visit(ConfigBoolElement element);
    // 新增类型时需要添加对应的Visit方法
}

abstract class ConfigElement
{
    // 原有成员...

    public abstract void Accept(IConfigElementVisitor visitor);
}

class ConfigStringElement : ConfigElement
{
    // 原有成员...

    public override void Accept(IConfigElementVisitor visitor) => visitor.Visit(this);
}

class ConfigBoolElement : ConfigElement
{
    // 原有成员...

    public override void Accept(IConfigElementVisitor visitor) => visitor.Visit(this);
}

创建访问者获取值:

public class ConfigValueGetter<T> : IConfigElementVisitor
{
    public T Value { get; private set; }
    public bool Success { get; private set; }

    public void Visit(ConfigStringElement element)
    {
        if (typeof(T) == typeof(string))
        {
            Value = (T)(object)element.GetValue();
            Success = true;
        }
    }

    public void Visit(ConfigBoolElement element)
    {
        if (typeof(T) == typeof(bool))
        {
            Value = (T)(object)element.GetValue();
            Success = true;
        }
    }
}

使用方式:

var userNameGetter = new ConfigValueGetter<string>();
configDict["UserName"].Accept(userNameGetter);
if (userNameGetter.Success)
{
    var userName = userNameGetter.Value;
}

方案对比

  • 泛型基类:类型安全,代码结构清晰,适合明确知道配置类型的场景,但需要显式转换。
  • TryGetValue方法:灵活性高,无需提前知道类型,适合动态场景,但要处理转换失败的情况。
  • 访问者模式:扩展性好,适合需要对不同类型配置执行多种操作的场景,但新增配置类型时需修改访问者接口。

内容的提问来源于stack exchange,提问作者Jordan Larson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 23:55:01