如何从配置文件重构高度重复的读取部分

时间:2011-07-12 21:18:29

标签: c# templates configuration refactoring

我需要将配置文件的多个部分转换为字典。这些词典的值有不同的类型。以下两个类可以工作,但它们几乎完全相同:

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 =&gt; val最简单的lambda什么都不做?

3 个答案:

答案 0 :(得分: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.ChangeType单独为每种类型指定Convert函数:

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

答案 1 :(得分:3)

你可以直接传入一个实际解析该类型的函数:(注意:未经测试的代码,考虑这个想法而不是工作代码: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);

答案 2 :(得分:2)

这是一个常见问题,您最终会遇到一定程度的重复。诀窍是弄清楚你可以做多少小的重复以及你想要在哪里进行本地化。在这个特定的例子中,您可以:

  1. Func<string, T> parseMethod参数传递给通用类构造函数。这允许您支持任何类型,甚至允许对不同的配置节实例以不同方式解析相同类型。
  2. 获取该类型的TypeConverter并尝试从字符串转换。
  3. 要求类型支持IConvertible并转换为/来自字符串。
  4. 使用反射在类型上查找静态Parse或TryParse方法。这并不理想,但BCL有使用类似hacky'魔法方法名'的例子。