Disclaimer None - these are my opinions and they're also my employer's!
| |
E-mail
Sign In
Ever had one of those moments when you wished you either knew everything there was to know about software development or had decided to become a pea farmer in Brazil (not that they even have pea farms in Brazil, but they might, but....) instead of a software developer. Well, I have and a show of hands in a crowded technical gathering would prove my point. This self-realization should give the reader enough pause about reading on without me clearly stating that I'm sure someone else has solved this problem in a much more elegant way - after all, I'm not even sure they farm peas in Brazil.
Anyway, just when I had decided that I should've given myself more time when I made that fateful "farming vs. programming" decision, along comes an Intellisense moment which made it all better. I knew about the Convert class and had used it numerous times so I did the fateful "conv+tab+." and started looking for gold. Sure enough I found it in the ChangeType() method.
A fellow developer had written a handy generic method for the type friendly retrieval of application configuration settings. Everything was working well until I tempted Fate and asked for a value type, int in this case, back from the method:
Error 1 The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'ConsoleApplication1.Program.GetSetting<T>(string)'
Well, that seemed like an important capability to have so diving into the wrapper I went:
public static T GetSetting<T>(string key) where T : class
{
return ConfigurationManager.AppSettings[key] as T;
}
To start with there was no way to call this method passing int as the generic type because of the class constraint. So, I quickly modified it to the following:
public static T GetSetting<T>(string key)
Which, of course, just means I'm a lazy programmer (which, of course, translates to reduced crops of peas if I had chosen that profession.) At first glance, I thought removing the class constraint would be the answer. Since we don't have to think things out all the time in this age of change/compile/test/change/compile/test/scream/change/compile/test development I let the compiler tell me if my guess was wrong (isn't that the whole point of generics anyway?) kind of like a game of "Who Wants to Be a Millionaire?" without the lifelines:
Error 1 The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint
This error message was so useful since I had just purposefully removed the class constraint. The super-fast-non-thinking-programmer in me changed it to:
return (T)ConfigurationManager.AppSettings[key];
The very brief moment of insight which caused me to change the cool and friendly as cast to the traditional brute force cast proved NOT to be insightful at all as the trusty compiler let me know:
Error 1 Cannot convert type 'string' to 'T'
And I thought you could cast pretty easily in the .NET Framework. Well, you can, but the compiler has no idea what T is going to be so it stops you from making a bad cast and getting a runtime error which of course is apparent evil. Strong-typing vs. dynamic-typing aside, we were in a real pickle. We needed this generic method to deal well with a variety of types so...
return (T)Convert.ChangeType(ConfigurationManager.AppSettings[key], typeof(T));
ChangeType() has several overloads but the one I was interested in took two parameters: 1) object value and 2) Type. This was exactly what was needed and I had another moment of "I am invincible" and was sure that I had chosen the right pill (talk about mixing movie metaphors.)
Everything compiled and my unit tests proved it to be working swimmingly so what was that gnawing concern growing in the pit of my "been around the bend a few times" stomach. Things seemed happy and well adjusted. That's about the time I always have hair stand up on the back of my neck. What's the catch? Performance? Odd inconsistencies that will absolutely drive me nuts later? Deprecation in the next release of the framework?
Armed with nothing but paranoia, I decided to research this little bad boy and see just how awful it was. Come to find out my worries weren't too justified as I found out while Reflectoring the method in mscorlib.dll. They actually did what my cohort and I thought we were going to have to do in examining the incoming T parameter's type and finding the right translation from the string results of the ConfigurationManager.AppSettings[key]. So maybe I'm not as crazy as I thought since the framework folks did just what I had planned.
However, one thing jumped out when I looked closely at the disassembly:
IConvertible convertible1 = value as IConvertible;
if (convertible1 == null)
if (value.GetType() != conversionType)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_IConvertible"));
return value;
As I pondered the deeper meaning of what I had just found I realized that it might be a bad thing if I passed in a type parameter for T which was not of the IConvertible ilk. Which led me to the final version below:
public static T GetSetting<T>(string key) where T : IConvertible
With the IConvertible constraint on the T parameter I could rest easy that this method should provide years of use without any harmful side effects and that I could lay to rest the ongoing controversy in my mind of peas or C# (yeah, right.)