【问题标题】:Best way to create a strongly typed wrapper for Dictionary<string, string>为 Dictionary<string, string> 创建强类型包装器的最佳方法
【发布时间】:2011-03-12 10:14:05
【问题描述】:

我有一个字典,其中包含其他类的配置值(将定期执行的任务,执行各种专门的逻辑),这些值保存在数据库中,然后在执行时传回。

我想为这个字典创建一个强类型的包装器,既可以方便地访问值,也可以将它们转换为正确的类型。

目前我有这样的事情:

public class ConfigurationWrapper {

    Dictionary<string, string> _configuration;

    public ConfigurationWrapper(Dictionary<string, string> configuration) {
        _configuration = configuration;
        InitializeDefaultValues();
    }

    public virtual GetConfigurationCopy() {
        return new Dictionary(_configuration);
    }

    protected InitializeDefaultValues() {
        Type t = GetType();

        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(t);
        foreach (PropertyDescriptor property in properties) {
            AttributeCollection attributes = property.Attributes;
            DefaultValueAttribute defaultValue = (DefaultValueAttribute)attributes[typeof(DefaultValueAttribute)];
            if (defaultValue != null) {
                 if (!Configuration.ContainsKey(property.Name)) {
                 Configuration[property.Name] = Convert.ToString(defaultValue.Value, CultureInfo.InvariantCulture);
                 }
            }
        }
    }
}

public class MyTaskConfigurationWrapper : ConfigurationWrapper {
    private const string MyIntPropertyKey = "MyIntProperty";
    [DefaultValue(7)]
    int MyIntProperty {
        get { return Convert.ToInt32(_configuration[MyIntPropertyKey], CultureInfo.InvarientCulture); }
        set { _configuration[MyIntPropertyKey] = value.ToString(CultureInfo.InvarientCulture); }
    }

    // More properties of various types.
}

我的问题是是否有办法改进这种设计。

我考虑过的一件事是使用反射来获取属性的名称(以及配置值),正如here 所讨论的那样。这省去了创建字符串键并隐式强制键与属性同名(这是InitializeDefaultValues() 代码运行所必需的),但它也掩盖了这样一个事实,即如果更改了属性名称,则配置值的名称将更改。所以这是一个权衡。

这看起来像下面这样:

// Could alternately use PropertyHelper example with some compile time checking
protected string GetProperty(MethodBase getMethod) {
    if (!getMethod.Name.StartsWith("get_") {
        throw new ArgumentException(
            "GetProperty must be called from a property");
    }
    return _configuration[getMethod.Name.Substring(4)];
}

protected string SetProperty(MethodBase getMethod, string value) {
    // Similar to above except set instead of get
}

[DefaultValue(7)]
int MyIntProperty {
    get { return Convert.ToInt32(GetProperty(MethodInfo.GetCurrentMethod(), CultureInfo.InvarientCulture); }
    set { SetProperty(MethodInfo.GetCurrentMethod(), value.ToString(CultureInfo.InvarientCulture); }
}

【问题讨论】:

    标签: c# properties strong-typing


    【解决方案1】:

    除非您对默认值属性有其他用途,否则我会避免使用整个反射方法,而是使用一个扩展类来执行值转换并且您可以在其中提供默认值。您可以将类型转换直接捆绑到扩展方法中,以避免显式调用Convert.ToXXX()

    public static class DictionaryExt
    {
        // bundles up safe dictionary lookup with value conviersion...
        // could be split apart for improved maintenance if you like...
        public static TResult ValueOrDefault<TKey,TValue,TResult>( 
            this DIctionary<TKey,TValue> dictionary, TKey key, TResult defaultVal )
        {
            TValue value;
            return dictionary.TryGetValue( key, out value ) 
              ? Convert.ChangeType( value, typeof(TResult) )
              : defaultVal;
        }
    }
    

    现在你的配置类可能是:

    public class MyTaskConfigurationWrapper : ConfigurationWrapper 
    {  
        private const string MyIntPropertyKey = "MyIntProperty";  
    
        int MyIntProperty 
        {
            // supply the default as a parameter, not reflection attribute
          get {
            return _config.ValueOrDefault( MyIntPropertyKey, 7 ); } 
          set {
         _config[MyIntPropertyKey] = value.ToString(CultureInfo.InvarientCulture); }
        }  
    
        // More properties of various types.  
    }  
    

    这种方法允许您为没有方便的编译时间表示的类型(如 DateTime、TimeSpan、Point 等)提供默认值。它还避免了所有凌乱的反射逻辑。

    如果您需要通过基类访问默认值,那么您可能需要使用反射/属性方法。但即便如此,您也可以通过遍历所有公共属性并访问它们的 getter 以在创建时获取默认值来简单地使用默认值初始化值字典。

    【讨论】:

    • 我之前没有遇到过 Convert.ChangeType() 。感谢您指出;这将非常有帮助。其余的建议也很有用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多