2010年8月4日 星期三

Maybe as an Extension Method

Maybe as an Extension Method

by KodefuGuru 30. June 2010 18:13

A frequent question I received concerning the Maybe static class was “why didn’t you make extension methods?” I am a proponent of extension methods, so it would seem that I would prefer “42”.ToInt32() over Maybe.ToInt32(“42”). However, I immediately created a static class for this despite my previous criticisms of helper classes. Why?

Semantics

What does it mean when someone writes Request.QueryString[“index”].ToInt32()? It’s not obvious that ToInt32() returns a Nullable<int>. When I first created the Maybe class, I wanted to call it Nullable but couldn’t do so because a non-generic Nullable existed in the framework. Maybe was the next best thing: it may be an Int32, in may be null.

Responsibility

There is another problem with making these extension methods. It is far too easy to give String too much responsibility. I try to be careful when making decisions about assigning anything to a string. I fully expect someone to create a full string domain specific language with extension methods in the near future.

Extension

Okay, so I did decide to make an extension method. I felt the methods on Maybe were candidates, and it would be easy enough to add “this” to the first parameter, but I wanted to maintain semantics. What I opted for was a Maybe<T> method on string.

public static Nullable<T> Maybe<T>(this string value) where T : struct { var maybe = new Dictionary<Type, Func<string, ValueType>> { { typeof(BigInteger), s => Kodefu.Maybe.ToBigInteger(s) }, { typeof(Boolean), s => Kodefu.Maybe.ToBoolean(s) }, { typeof(Byte), s => Kodefu.Maybe.ToByte(s) }, { typeof(DateTime), s => Kodefu.Maybe.ToDateTime(s) }, { typeof(Decimal), s => Kodefu.Maybe.ToDecimal(s) }, { typeof(Double), s => Kodefu.Maybe.ToDouble(s) }, { typeof(Enum), s => Kodefu.Maybe.ToEnum<T>(s) }, { typeof(Guid), s => Kodefu.Maybe.ToGuid(s) }, { typeof(Int16), s => Kodefu.Maybe.ToInt16(s) }, { typeof(Int32), s => Kodefu.Maybe.ToInt32(s) }, { typeof(Int64), s => Kodefu.Maybe.ToInt64(s) }, { typeof(SByte), s => Kodefu.Maybe.ToSByte(s) }, { typeof(Single), s => Kodefu.Maybe.ToSingle(s) }, { typeof(TimeSpan), s => Kodefu.Maybe.ToTimeSpan(s) }, { typeof(UInt16), s => Kodefu.Maybe.ToUInt16(s) }, { typeof(UInt32), s => Kodefu.Maybe.ToUInt32(s) }, { typeof(UInt64), s => Kodefu.Maybe.ToUInt64(s) }, };  Type type = typeof(T); type = type.IsEnum ? type.BaseType : type;  if (maybe.ContainsKey(type)) { return maybe[type](value) as Nullable<T>; }              return null; }

** I updated the code base to move the dictionary out of the method. Creating a dictionary was unnoticeable in the operation, but I suspect someone will complain. grab the updated version of you don’t like this one.

This started out as one big set of if statements. However, it was nearly unreadable so I refactored to a dictionary. The fully qualified domain name it necessary because the method name is Maybe<T>, and there are conflicts even though the Maybe static class has no generic parameter. Which brings me to another C# lesson: you cannot add this as an extension method in the Maybe class because it has the same name as the class. I put it under String extensions.

If you’re wondering about the ValueType and the odd cast… well, without generic inference, the compiler couldn’t figure out that I was returning a Nullable<T>. I decided to cast in one place.

If you prefer the extension method way of doing things, you can call Request.QueryString[“index”].Maybe<int>() ?? 0. I still prefer helper class way of doing things, but it’s an option!

Download the source code at kodefu.codeplex.com.

Posted via email from CodeBetter

0 意見: