Лучший способ для создания строго типизированной оболочки для словаря <строка, строка>

У меня есть значения, содержащие конфигурации словаря для других классов (задачи, которые будут выполняться периодически выполняя ассорти специализированной логики), которые сохранялись в базе данных, а затем передаются обратно в во время выполнения.

Я хочу создать строго типизированную оболочку для этого словаря, чтобы обеспечить легкий доступ к значениям и привести их к нужному типу.

На данный момент у меня есть что-то вроде этого:

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

Мой вопрос, если есть способ, чтобы улучшить эту конструкцию.

Одна вещь, которую я рассмотрел, — это использование отражения для получения имени свойства (и, следовательно, значения конфигурации), как обсуждалось . Это избавляет от необходимости создавать строковый ключ и неявно заставляет ключ иметь то же имя, что и свойство (что требуется для работы кода 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); }
}

person Lawrence Johnston    schedule 06.07.2010    source источник


Ответы (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 и т. д.). Это также позволяет избежать всей грязной логики отражения.

Если вам необходимо получить доступ к значениям по умолчанию с помощью базового класса, то вы можете использовать подход отражения / атрибут. Но даже, можно просто инициализировать словарь значения со значениями по умолчанию перебирает все открытые свойства и доступ к их поглотитель для извлечения значения по умолчанию во время создания.

person LBushkin    schedule 06.07.2010
comment
Я раньше не сталкивался с Convert.ChangeType(). Спасибо, что указали на это; это будет очень полезно. Остальная часть предложения также полезна. - person Lawrence Johnston; 06.07.2010