Navigation

Search

Categories

On this page

Archive

Blogroll

Disclaimer
None - these are my opinions and they're also my employer's!

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 14
This Year: 0
This Month: 0
This Week: 0
Comments: 15

Sign In

 Tuesday, December 05, 2006
Wednesday, December 06, 2006 3:31:37 AM (Mountain Standard Time, UTC-07:00) ( )

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.

The problem...

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)
{
   return ConfigurationManager.AppSettings[key] as T;
}

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:

public static T GetSetting<T>(string key)
{
   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...

The solution...(well, you read the title, right?)

public static T GetSetting<T>(string key)
{
   return (T)Convert.ChangeType(ConfigurationManager.AppSettings[key], typeof(T));
}

I am InvincibleChangeType() 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.)

The catch...

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
{
   return (T)Convert.ChangeType(ConfigurationManager.AppSettings[key], typeof(T));
}

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

Comments [3] | | # 
Tuesday, December 04, 2007 1:51:49 PM (Mountain Standard Time, UTC-07:00)
very nice
Thursday, December 06, 2007 2:16:06 AM (Mountain Standard Time, UTC-07:00)
Thank you very much, I managed to let a learning opportunity!
Saturday, February 02, 2008 3:39:34 PM (Mountain Standard Time, UTC-07:00)
Cool, the post.

Thanks for the information.
Comments are closed.