2010-07-06 1 views
0

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

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

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

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

Если у вас нет других целей, для атрибута значения по умолчанию, я хотел бы избежать весь подход отражения, и вместо того, чтобы использовать класс расширения, который выполняет преобразование значения и где вы можете указать значения по умолчанию. Вы можете связать преобразование типа непосредственно с методом расширения, чтобы избежать необходимости делать явные вызовы 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, точки и т.д.) , Он также избегает всей беспорядочной логики отражения.

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

+0

Я раньше не встречал Convert.ChangeType(). Спасибо, что указали это; это будет очень полезно. Остальное предложение полезно. –