I want to parse a string into a type easily, but I don\'t want to write wrapper code for each type, I just want to be able to do \"1234\".Parse() or the like and have it return
My solution works for any type that implements the static method TryParse(string, out T), whether it's a class or a struct. Also, it will work for nullable structs, e.g.
"1234".Parse() == 1234
"asdf".Parse() == 0 // i.e. default(int)
"1234".Parse() == 1234
"asdf".Parse() == null
"2001-02-03".Parse() == new DateTime(2009, 2, 3)
and since System.Net.IPAddress has TryParse,
"127.0.0.1".Parse().Equals(new IPAddress(new byte[] { 127, 0, 0, 1 }))
I create the code with the System.Linq.Expressions framework, and then cache the created lambda. Since this is done in a generic static class with a specified type, this happens only once per type to parse.
public static class StringExtensions
{
///
/// Parse the as a . If this cannot be achieved, return the default value of .
///
/// The type to parse into.
/// The string to parse.
/// The resultant or the default of .
///
///
/// "1234".Parse<int>() == 1234;
/// "a".Parse<int>() == 0;
/// "a".Parse<int?>() == null;
/// "2010-01-01".Parse<DateTime?>() == new DateTime(2010, 1, 1)
/// "2010-01-01a".Parse<DateTime?>() == null
/// "127.0.0.1".Parse<System.Net.IPAddress>().Equals(new System.Net.IPAddress(new byte[] { 127, 0, 0, 1 }))
/// "".Parse<System.Net.IPAddress>() == null
///
///
public static T Parse(this string target)
{
return ParseHelper.Parse(target);
}
///
/// Parse the as a . If this cannot be achieved, return
///
/// The type to parse into.
/// The string to parse.
/// The value to return if could not be parsed.
/// The resultant or .
///
///
/// "1234".Parse<int>(-1) == 1234;
/// "a".Parse<int>(-1) == -1;
/// "2010-01-01".Parse<DateTime?>(new DateTime(1900, 1, 1)) == new DateTime(2010, 1, 1)
/// "2010-01-01a".Parse<DateTime?>(new DateTime(1900, 1, 1)) == new DateTime(1900, 1, 1)
/// "127.0.0.1".Parse<System.Net.IPAddress>(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 })).Equals(new System.Net.IPAddress(new byte[] { 127, 0, 0, 1 }))
/// "".Parse<System.Net.IPAddress>(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 })).Equals(new System.Net.IPAddress(new byte[] { 0, 0, 0, 0 }))
///
///
public static T Parse(this string target, T defaultValue)
{
return ParseHelper.Parse(target, defaultValue);
}
private static class ParseHelper
{
private static readonly Func _parser;
static ParseHelper()
{
Type type = typeof(T);
bool isNullable = false;
if (type.GetGenericArguments().Length > 0 && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
isNullable = true;
type = type.GetGenericArguments()[0];
}
ParameterExpression target = Expression.Parameter(typeof(string), "target");
ParameterExpression defaultValue = Expression.Parameter(typeof(T), "defaultValue");
ParameterExpression result = Expression.Parameter(typeof(T), "result");
if (isNullable)
{
Type helper = typeof(NullableParseHelper<>);
helper = helper.MakeGenericType(typeof(T), type);
MethodInfo parseMethod = helper.GetMethod("Parse");
_parser = Expression.Lambda>(
Expression.Call(parseMethod, target, defaultValue),
target, defaultValue, result).Compile();
}
else
{
MethodInfo tryParseMethod = (from m in typeof(T).GetMethods()
where m.Name == "TryParse"
let ps = m.GetParameters()
where ps.Count() == 2
&& ps[0].ParameterType == typeof(string)
&& ps[1].ParameterType == typeof(T).MakeByRefType()
select m).SingleOrDefault();
if (tryParseMethod == null)
{
throw new InvalidOperationException(string.Format("Cannot find method {0}.TryParse(string, out {0})", type.FullName));
}
_parser = Expression.Lambda>(
Expression.Condition(
Expression.Call(tryParseMethod, target, result),
result,
defaultValue),
target, defaultValue, result).Compile();
}
}
public static T Parse(string target)
{
return _parser.Invoke(target, default(T), default(T));
}
public static T Parse(string target, T defaultValue)
{
return _parser.Invoke(target, defaultValue, default(T));
}
private static class NullableParseHelper where TBase : struct
{
private static readonly Func _parser;
static NullableParseHelper()
{
MethodInfo tryParseMethod = (from m in typeof(TBase).GetMethods()
where m.Name == "TryParse"
let ps = m.GetParameters()
where ps.Count() == 2
&& ps[0].ParameterType == typeof(string)
&& ps[1].ParameterType == typeof(TBase).MakeByRefType()
select m).SingleOrDefault();
if (tryParseMethod == null)
{
throw new InvalidOperationException(string.Format("Cannot find method {0}.TryParse(string, out {0})", typeof(TBase).FullName));
}
ParameterExpression target = Expression.Parameter(typeof(string), "target");
ParameterExpression defaultValue = Expression.Parameter(typeof(TBase?), "defaultValue");
ParameterExpression result = Expression.Parameter(typeof(TBase), "result");
_parser = Expression.Lambda>(
Expression.Condition(
Expression.Call(tryParseMethod, target, result),
Expression.ConvertChecked(result, typeof(TBase?)),
defaultValue),
target, defaultValue, result).Compile();
}
public static TBase? Parse(string target, TBase? defaultValue)
{
return _parser.Invoke(target, defaultValue, default(TBase));
}
}
}
}
pants!