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

本文关键字:高度 读取部 配置文件 何重构 重构 | 更新日期: 2023-09-27 18:01:58

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

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吗?

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

我的建议如下:

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

您可以传入一个实际对该类型进行解析的函数:(注意:未测试的代码,将其视为一个想法而不是工作代码: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);

这是一个常见的问题,您总是以某种程度的重复结束。关键是要弄清楚复制的大小以及本地化的位置。在这个特定的示例中,您可以:

  1. 传递一个Func<string, T> parsemmethod参数到你的泛型类构造函数。这允许你支持任何类型,甚至允许同一类型以不同的方式解析不同的配置节实例。
  2. 获取类型的TypeConverter,并尝试从字符串转换。
  3. 要求类型支持IConvertible,并且可以从string中转换。
  4. 使用反射来查找类型上的静态Parse或TryParse方法。这并不理想,但是BCL有使用类似的"魔法方法名"的例子。