Friday, July 25, 2008

Lazy TryParse

This is my second attempt on the TryParse with the C# 3.0 Extension Methods.

I just noticed that I am copy-pasting a lot of the Integer and Boolean parsing in my current project. Maybe I just don't know any better but I ended up using TryParse this way:

int intParsed;
value= int.TryParse(reader["Value"].ToString(), out intParsed) ? intParsed : 0;

or a little bit simpler:

int intParsed= int.TryParse(reader["Value"].ToString(), out intParsed) ? intParsed : 0;

I will appreciate if somebody would show me simpler solution but so far it is as it is. I am stuck with .NET 2.0 for now, but would it be 3.5, I'd put together an extension method. It started as a pretty simple extension of a Int32 type, but quickly evolved to the more generic solution, which uses some kind of Duck Typing guessing:

public static class TryParseExtender
{
public static T LazyTryParse<T>(this T instance, object input)
{
Type type = typeof (T);
MemberInfo[] members = type.FindMembers(
MemberTypes.Method,
BindingFlags.Public | BindingFlags.Static,
(objMemberInfo, objSearch) => objMemberInfo.Name.Equals(objSearch.ToString()),
"TryParse"
);
foreach (MemberInfo info in members)
{
try
{
bool boolResult;
object[] paramArray=new[]{input, instance};
object objResult = ((MethodInfo) info).Invoke(instance, paramArray);
if (bool.TryParse(objResult.ToString(), out boolResult)) return (T)paramArray[1];
break;
}
catch {}
}
throw new ApplicationException(type+ " doesn't support TryParse");
}
}
It is not the prettiest code but does the job. Return statement assumes that types which expose TryParse are able to cast the Object type to themselves. Unfortunately the C# Extension Methods are not that powerful as the Ruby's Monkey Patching and I am already far enough into the Ruby to recognize what power (and elegance) I am missing.

Here is the test harness for the extension, which also shows the usage patterns (generic inference makes code look simpler):

[TestFixture]
public class TryParseExtenderTest
{
[Test]
public void TestIntParsing()
{
string input = "230";
int result = 0;
result = result.LazyTryParse(input);
Assert.AreEqual(230, result);
}

[Test, ExpectedException(typeof(ApplicationException),
ExpectedMessage = "System.String doesn't support TryParse")]
public void TestStringParsing()
{
int input = 230;
string result = "";
result = result.LazyTryParse(input);

Assert.AreEqual("230", result);
}

[Test]
public void TestBooleanParsing()
{
string input = "true";
bool result = true;
result = result.LazyTryParse(input);
Assert.AreEqual(true, result);
}

[Test]
public void TestDateTimeParsing()
{
string input = new DateTime(2008, 12, 1).ToString();
DateTime result = DateTime.Now;
result = result.LazyTryParse(input);
Assert.AreEqual(new DateTime(2008, 12, 1), result);
}
}

2 comments:

Chris Barrow said...

Nice attempt Ruby wantatbe. Why don't you just complete the conversion and go completely Ruby all the way?

Michael Goldobin said...

I am supporter of the Communist Programming Paradigm - strong static types, restrictive models, predictable inheritance, verbose assemblies.


© 2008-2013 Michael Goldobin. All rights reserved