Reputation: 2446
I need to convert multiple sections of config file to dictionaries. Values of those dictionaries have different types. The following two classes work, but they are almost identical:
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;
}
}
The following code does not work as required, but it demonstrates what I am trying to accomplish:
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;
}
}
Edit: my final version looks as follows:
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;
}
}
My only concern is this: is val => val the simplest lambda that does nothing?
Upvotes: 1
Views: 321
Reputation: 35594
I would propose the following:
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;
}
}
(Disclaimer: I didn't try the code!)
EDIT:
It should be possible to avoid specifying the Convert
function for each type individually by using 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)));
...
}
}
Upvotes: 5
Reputation: 3247
you could just pass in a function that actually does the parsing for that type: (note: untested code, consider this an idea rather than working code :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;
}
}
and then pass in your actual parser as a lambda (or similar..)
or you could actually include the parser in a constructor for the class and then have it be a member so you don't have to pass it in every time.
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;
}
}
and you could call it like so:
ConfigSection<int> = new ConfigSection<int>(int.Parse);
Upvotes: 3
Reputation: 27505
This is a common problem and you always end up with some degree of duplication. The trick is figuring out how small you can make the duplication and where you want to localize it. In this particular example, you could:
Func<string, T>
parseMethod parameter into your generic class constructor. This allows you to support any type and even allows the same type to be parsed differently for different config section instances.Upvotes: 2