为 Dictionary<string, string> 创建强类型包装器的最佳方法 [英] Best way to create a strongly typed wrapper for Dictionary&lt;string, string&gt;

查看:50
本文介绍了为 Dictionary<string, string> 创建强类型包装器的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a Dictionary containing configuration values for other classes (tasks which will be executed periodically performing assorted specialized logic) which are persisted in a database and then passed back in at execution time.

我想为此字典创建一个强类型包装器,以便轻松访问值并将它们转换为正确的类型.

I want to create a strongly typed wrapper for this Dictionary, both to allow easy access to the values and to cast them to the proper type.

目前我有这样的事情:

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

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

My question is if there's a way to improve this design.

我考虑过的一件事是使用反射来获取属性的名称(以及配置值),正如所讨论的 此处.这无需创建字符串键并隐式强制键与属性具有相同的名称(这是 InitializeDefaultValues() 代码运行所必需的),但它也掩盖了这样一个事实就是这种情况,如果属性名称更改,配置值的名称也会更改.所以这是一个权衡.

One thing I've considered is using reflection to get the name for the property (and hence the configuration value) as discussed here. This saves having to create a string key and implicitly forces the key to have the same name as the property (which is required for the InitializeDefaultValues() code to function), but it also obscures the fact that this is the case and that that the name of the configuration value will change if the name of the property is changed. So it's a trade off.

这将类似于以下内容:

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

推荐答案

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

Unless you have other purposes for the default value attribute, I would avoid the entire reflection approach, and instead use an extension class that performs value conversion and where you can supply the default values. You could bundle up the type conversion directly into the extension method to avoid having to do explicit calls to 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;
    }
}

现在你的配置类可以是:

Now your configuration class could be:

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 等)提供默认值.它还避免了所有杂乱的反射逻辑.

This approach allows you to supply default values to types that don't have convenient compile time representations (like DateTime, TimeSpan, Point, etc). It also avoid all of the messy reflection logic.

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

If you need to access the default values via the base class, then you may want to use the reflection/attribute approach. But even the, you could simply initialize your value dictionary with defaults by iterating over all public properties and accessing their getter to fetch the default value at creation time.

这篇关于为 Dictionary<string, string> 创建强类型包装器的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆