2011-07-12 18 views
1

我需要将配置文件的多个部分转换为字典。这些字典的价值有不同的类型。以下两类工作,但他们几乎是相同的:如何从配置文件重构高度重复的阅读部分

public class IntConfigSection 
{ 
    private static readonly ILog Log = LogManager.GetLogger(typeof(IntConfigSection)); 
    public static Dictionary<String, int> LoadSection(string sectionName) 
    { 
     var ret = new Dictionary<String, int>(); 
     try 
     { 
      var offsetsHash = (Hashtable)ConfigurationManager.GetSection(sectionName); 
      foreach (DictionaryEntry entry in offsetsHash) 
      { 
       ret.Add((String)entry.Key, int.Parse((String)entry.Value)); 
      } 
     } 
     catch(Exception e) 
     { 
      Log.ErrorFormat("LoadSection:" + e); 
     } 
     return ret; 
    } 
} 

public class StringConfigSection 
{ 
    private static readonly ILog Log = LogManager.GetLogger(typeof(StringConfigSection)); 
    public static Dictionary<String, String> LoadSection(string sectionName) 
    { 
     var ret = new Dictionary<String, String>(); 
     try 
     { 
      var offsetsHash = (Hashtable)ConfigurationManager.GetSection(sectionName); 
      foreach (DictionaryEntry entry in offsetsHash) 
      { 
       ret.Add((String)entry.Key, (String)entry.Value); 
      } 
     } 
     catch (Exception e) 
     { 
      Log.ErrorFormat("LoadSection:" + e); 
     } 
     return ret; 
    } 
} 

要求下面的代码不能正常工作,但它证明了什么,我试图完成:

public class ConfigSection<T> 
{ 
    private static readonly ILog Log = LogManager.GetLogger(typeof(StringConfigSection)); 
    public static Dictionary<String, T> LoadSection(string sectionName) 
    { 
     var ret = new Dictionary<String, T>(); 
     try 
     { 
      var offsetsHash = (Hashtable)ConfigurationManager.GetSection(sectionName); 
      foreach (DictionaryEntry entry in offsetsHash) 
      { 
       //builds but does not always do what I want 
       ret.Add((String)entry.Key, (T)entry.Value); 
       // does not compile 
       //ret.Add((String)entry.Key, T.Parse((String)entry.Value)); 
      } 
     } 
     catch (Exception e) 
     { 
      Log.ErrorFormat("LoadSection:" + e); 
     } 
     return ret; 
    } 
} 

编辑:我的最终版本看起来如下:

public class ConfigSectionLoader 
{ 
    public static Dictionary<String, int> LoadIntSection(string sectionName) 
    { 
     return ConfigSection<int>.LoadSection(sectionName, int.Parse); 
    } 

    public static Dictionary<String, String> LoadStringSection(string sectionName) 
    { 
     return ConfigSection<String>.LoadSection(sectionName, val => val); 
    } 
} 

internal class ConfigSection<T> 
{ 
    private static readonly ILog Log = LogManager.GetLogger(typeof(StringConfigSection)); 
    internal static Dictionary<String, T> LoadSection(string sectionName, Func<String, T> parseFunc) 
    { 
     var ret = new Dictionary<String, T>(); 
     try 
     { 
      var hash = (Hashtable)ConfigurationManager.GetSection(sectionName); 
      foreach (DictionaryEntry entry in hash) 
      { 
       ret.Add((String)entry.Key, parseFunc((String)entry.Value)); 
      } 
     } 
     catch (Exception e) 
     { 
      Log.ErrorFormat("LoadSection:" + e); 
     } 
     return ret; 
    } 
} 

我唯一关心的是:val => val最简单的lambda什么也不做?

+0

哦;抱歉,阿恩,我没有注意到你已经更新了你的主要问题,我在下面误解了你的问题。我想我会以不同的方式说出来。但如果我理解你的问题,那么我会说是,val => val是做这种类型的“传递”的方式,在那里你需要lambda的结果与参数相同。:) – shelleybutterfly

回答

3

你可以传递一个函数, ES该类型解析:(注:未经测试的代码,认为这是一个想法,而不是工作代码:d)

public class ConfigSection<T> 
{ 
    private static readonly ILog Log = LogManager.GetLogger(typeof(StringConfigSection)); 
    public static Dictionary<String, T> LoadSection(string sectionName, Func<String,T> parseFunc) 
    { 
     var ret = new Dictionary<String, T>(); 
     try 
     { 
      var offsetsHash = (Hashtable)ConfigurationManager.GetSection(sectionName); 
      foreach (DictionaryEntry entry in offsetsHash) 
      { 
       ret.Add((String)entry.Key, parseFunc((String)entry.Value)); 
      } 
     } 
     catch (Exception e) 
     { 
      Log.ErrorFormat("LoadSection:" + e); 
     } 
     return ret; 
    } 
} 

,然后传递到自己的实际解析器一个lambda(或类似的..)

或者你实际上可以将解析器包含在类的构造函数中,然后让它成为一个成员,所以你不必每次都传递它。

public class ConfigSection<T> 
{ 
    private Func<String, T> myParseFunc = null; 

    public ConfigSection<T>(Func<String,T> parParseFunc) 
    { 
     myParseFunc = parParseFunc; 
    } 

    private static readonly ILog Log = LogManager.GetLogger(typeof(StringConfigSection)); 
    public static Dictionary<String, T> LoadSection(string sectionName) 
    { 
     var ret = new Dictionary<String, T>(); 
     try 
     { 
      var offsetsHash = (Hashtable)ConfigurationManager.GetSection(sectionName); 
      foreach (DictionaryEntry entry in offsetsHash) 
      { 
       ret.Add((String)entry.Key, myParseFunc((String)entry.Value)); 
      } 
     } 
     catch (Exception e) 
     { 
      Log.ErrorFormat("LoadSection:" + e); 
     } 
     return ret; 
    } 
} 

,你可以把它像这样:

ConfigSection<int> = new ConfigSection<int>(int.Parse); 
+0

更新了成员解析器的例子和示例用法 – shelleybutterfly

+1

你可以说'新的ConfigSection (int.Parse)'而不是'新的ConfigSection (val => int.Parse(val))' – Vlad

+0

:)谢谢;编辑示例 – shelleybutterfly

5

我想提出以下几点建议:

public abstract class ConfigSectionBase<T> 
{ 
    public static Dictionary<String, T> LoadSection(string sectionName) 
    { 
     ... 
     //builds but does not always do what I want 
     ret.Add((String)entry.Key, Convert((string)entry.Value)); 
     ... 
    } 
    abstract T Convert(string v); 
} 

public class IntConfigSection: ConfigSectionBase<int> 
{ 
    override int Convert(string v) 
    { 
     return int.Parse(v); 
    } 
} 

public class StringConfigSection: ConfigSectionBase<string> 
{ 
    override string Convert(string v) 
    { 
     return v; 
    } 
} 

(声明:我没有尝试的代码)

编辑:
应该可以避免确定各类型的Convert功能单独使用Convert.ChangeType

public abstract class ConfigSection<T> 
{ 
    public static Dictionary<String, T> LoadSection(string sectionName) 
    { 
     ... 
     //builds but does not always do what I want 
     ret.Add((String)entry.Key, 
       (T)Convert.ChangeType((string)entry.Value, typeof(T))); 
     ... 
    } 
} 
+0

'Convert'方法的参数可能应该是'object'而不是'string',但除此之外,+1(或者在调用'Convert'之前应将值转换为字符串)。 – Groo

+0

@Groo:OP将'Value'转换为字符串,所以我认为它是一个字符串。我已更正了代码以包含演员表。 (编辑:采取您编辑的建议。) – Vlad

+0

@ Vlad:虽然这工作得很好,但我不确定我们是否真的需要一个额外的类,我们转换的类型。 –

2

这是一个普遍的问题,你总是有一定程度的重复结束。诀窍是弄清楚你可以做出多少小小的重复以及你想要将其本地化。在这个特定的例子中,你可以:

  1. 传递一个Func<string, T> parseMethod参数到你的泛型类的构造函数中。这允许您支持任何类型,甚至允许针对不同的配置节实例对相同类型进行不同的解析。
  2. 获取类型的TypeConverter并尝试从字符串转换。
  3. 要求类型支持IConvertible并将字符串转换为字符串。
  4. 使用反射来查找类型上的静态Parse或TryParse方法。这并不理想,但BCL有使用类似hacky'魔术方法名称'的例子。
+0

+1的想法:) – shelleybutterfly

+0

发誓,这是很多有用的想法。然而,使用反射似乎是一种矫枉过正,我宁愿自己指定转换,尤其是针对特定于本地的DateTime值。谢谢! –

+1

@Arne,IConvertible是一个很好的选择,如果你主要使用原始类型,你可以使用它作为通用约束,然后调用各种Convert静态方法来获得你想要的类型。反思肯定会成为最后的手段,但如果您需要与您无法控制的课程进行整合,则有时候会很方便。 –