2010-07-06 4 views
0

J'ai un dictionnaire contenant des valeurs de configuration pour d'autres classes (tâches qui seront exécutées périodiquement en effectuant une logique spécialisée) qui sont conservées dans une base de données puis renvoyées au moment de l'exécution.Meilleure façon de créer un wrapper fortement typé pour Dictionary <string, string>

Je souhaite créer un wrapper fortement typé pour ce dictionnaire, à la fois pour permettre un accès facile aux valeurs et pour les convertir au bon type.

Au moment où je quelque chose comme ceci:

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

Ma question est de savoir s'il y a un moyen d'améliorer cette conception. Une chose que j'ai considéré utilise la réflexion pour obtenir le nom de la propriété (et donc la valeur de configuration) comme discuté here. Cela évite d'avoir à créer une clé de chaîne et oblige implicitement la clé à avoir le même nom que la propriété (ce qui est requis pour que le code InitializeDefaultValues() fonctionne), mais cela obscurcit aussi le fait que c'est le cas et que le nom de la valeur de configuration changera si le nom de la propriété est modifié. C'est donc un compromis.

Cela ressemblerait à quelque chose comme ce qui suit:

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

Répondre

1

Sauf si vous avez d'autres fins pour l'attribut de valeur par défaut, j'éviterait l'approche de réflexion ensemble, et utiliser à la place une classe d'extension qui effectue la conversion de valeur et où vous pouvez fournir les valeurs par défaut. Vous pouvez regrouper la conversion de type directement dans la méthode d'extension pour éviter d'avoir à faire des appels explicites à 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; 
    } 
} 

Maintenant votre classe de configuration pourrait être:

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

Cette approche vous permet de fournir des valeurs par défaut à des types qui n'ont pas des représentations de temps de compilation pratique (comme DateTime, TimeSpan, point, etc.) . Il évite également toute la logique de réflexion désordonnée.

Si vous devez accéder aux valeurs par défaut via la classe de base, vous pouvez utiliser l'approche de réflexion/attribut. Mais même le, vous pouvez simplement initialiser votre dictionnaire de valeur avec les valeurs par défaut en itérant sur toutes les propriétés publiques et en accédant à leur getter pour récupérer la valeur par défaut au moment de la création.

+0

Je n'avais pas rencontré Convert.ChangeType() auparavant. Merci de l'avoir signalé. Ce sera très utile. Le reste de la suggestion est utile aussi. –

Questions connexes