代码之家  ›  专栏  ›  技术社区  ›  Redwood

为dictionary<string,string>

  •  0
  • Redwood  · 技术社区  · 14 年前

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

    我想为此字典创建一个强类型包装器,既允许轻松访问值,也允许将值强制转换为正确的类型。

    现在我有这样的东西:

    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); }
    }
    
    1 回复  |  直到 14 年前
        1
  •  1
  •   LBushkin    14 年前

    除非您对默认值属性有其他用途,否则我将避免使用整个反射方法,而是使用一个执行值转换的扩展类,并在其中提供默认值。可以将类型转换直接捆绑到扩展方法中,以避免对 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.  
    }  
    

    此方法允许您为没有方便的编译时表示形式(如日期时间、时间跨度、点等)的类型提供默认值。它还避免了所有混乱的反射逻辑。

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