I am attempting to write a configuration manager that can supply configuration settings from different providers (e.g. settings files, environment variables, databases etc).
I have envisioned that the settings will be either strings, ints or doubles and will be identified by a name
Settings are provided via classes implementing this:
public interface IConfigurationManagerProvider
{
T GetSetting<T>(string name);
IEnumerable<ConfigurationSetting> GetKnownSettings();
}
As a start I am trying to write a provider to return environment variables
public class EnvironmentVariableProvider : IConfigurationManagerProvider
{
public T GetSetting<T>(string name)
{
string value = Environment.GetEnvironmentVariable(name);
return value as T;
}
public IEnumerable<ConfigurationSetting> GetKnownSettings()
{
return new List<ConfigurationSetting>
{
new ConfigurationSetting("MY_TRACING", typeof (String)),
};
}
}
however this won't compile as it can't cast to T. If I put a class constraint in then this limits me to strings
public T GetSetting<T>(string name) where T : class
{
string value = Environment.GetEnvironmentVariable(name);
return value as T;
}
Really this provider can only supply strings (other providers will be different) however I want the code to access the configuration settings to be as clean as possible using something like:
public interface IConfigurationManagerConsumer
{
T GetConfiguration<T>(string name, T systemDefault);
}
and used like this:
string tracing = ConfigurationManager.GetConfiguration("MY_TRACING", "Unknown");
or this:
int maxUse开发者_开发百科rCount = = ConfigurationManager.GetConfiguration("Max User Count", 10);
or this:
double pi = ConfigurationManager.GetConfiguration("PI", 3.14159);
Am I going in the right direction?
use Convert.ChangeType
return (T)Convert.ChangeType(value, typeof(T));
I have a vaguely similar variable provider interface, in which I didn't use generics. I started out with generics, and found that it didn't make the code any more readable or performant, particularly since the types were few and known from the start. After all, you're not getting anything dynamic, it is inferring the type from the default value you give. I also wanted my variable provider to be usable without providing a default value, and didn't want to use a different syntax for that.
So mine consists of two levels. One is public interface provider that I use in code, which can be used like in these examples:
IVariableProvider var;
// providing default value
double pi = var.Double["pi", 3.14159];
// no default value, also bool is just an int, converted by the IVariableProvider.
bool answer = var.Bool["answer"];
int number = var.Int["number"];
if(var.Str.ContainsKey("text"))
{
string text = var.Str["text"];
...
}
I could add public interfaces for var.Float, var.Long, etc if desired.
Underneath, the IVariableProvider
can be given different "sources" to get variables from. The sources only expose string and integer. It can use other IVariableProvider
s as sources as well. When I call var.Double["pi"]
, it checks if any of the sources can provide a string variable for the key "pi" and then double.Parse()
and return it.
Ultimately, you can do it either way. If you use generics like that and then want to use it without default values, you'll have to use different syntax in order to specify the type.
精彩评论