Как реорганизовать очень повторяющиеся разделы чтения из конфигурационных файлов - PullRequest
1 голос
/ 13 июля 2011

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

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 - самая простая лямбда, которая ничего не делает?

Ответы [ 3 ]

5 голосов
/ 13 июля 2011

Я бы предложил следующее:

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

(Отказ от ответственности: я не пробовал код!)

РЕДАКТИРОВАТЬ:
Следует избегать указания *Функция 1007 * для каждого типа индивидуально с помощью 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)));
        ...
    }
}
3 голосов
/ 13 июля 2011

вы могли бы просто передать функцию, которая фактически выполняет синтаксический анализ для этого типа: (примечание: непроверенный код, считайте это идеей, а не рабочим кодом: 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;
    }
}

, а затем передайте свойфактический синтаксический анализатор в виде лямбды (или подобного ..)

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

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 голосов
/ 13 июля 2011

Это распространенная проблема, и вы всегда сталкиваетесь с некоторой степенью дублирования.Хитрость заключается в том, чтобы выяснить, насколько малым может быть дублирование и где вы хотите его локализовать.В этом конкретном примере вы можете:

  1. Передать параметр Func<string, T> parseMethod в ваш конструктор универсального класса.Это позволяет поддерживать любой тип и даже позволяет по-разному анализировать один и тот же тип для разных экземпляров раздела конфигурации.
  2. Получите TypeConverter для типа и попытайтесь выполнить преобразование из строки.
  3. Требуется, чтобыподдержка типов IConvertible и преобразование в / из строки.
  4. Используйте отражение, чтобы найти статический метод Parse или TryParse для типа.Это не идеально, но в BCL есть примеры использования похожих хакерских «имен магических методов».
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...