Steve Michelotti

C#, ASP.NET, and other stuff

  Home  |   Contact  |   Syndication    |   Login
  55 Posts | 1 Stories | 172 Comments | 52 Trackbacks

News






Google My Blog

Tag Cloud


Archives

Post Categories

Image Galleries

Articles

Blogs

When .NET 2.0 was released, a TryParse() method was added to many value types such as Int32, DataTime, etc. This was a huge improvement because there was no longer a need to catch expensive exceptions and drag down performance. However, one drawback of the TryParse() method is that the syntax is a little clunky. The most common example looks like this:

   1:  Person person = new Person();
   2:  string value = "21";
   3:  int num;
   4:  int.TryParse(value, out num);
   5:  person.Age = num;

So it's great that we can do all this without having to catch exceptions but it definitely seems more complex than it has to be given that all we're trying to do is assign an integer to the Age property and we need this many lines of code.  We can use Extension methods to make the consuming code much more friendly - a single line of code:

   1:  person.Age = value.TryParseInt32();

To implement the static class for the extension methods, we *could* implement each TryParseXX() method like this:

   1:  public static int TryParseInt32(this string value)
   2:  {
   3:      int result;
   4:      int.TryParse(value, out result);
   5:      return result;
   6:  }

However, now we've got the ugly, repetitive code in numerous methods inside that class. On the good side, at least we've encapsulated the repetitive code inside a single class, but we can leverage a generic delegate and generic method to even avoid the repetition inside that class.

   1:  private delegate bool ParseDelegate<T>(string s, out T result);
   2:   
   3:  private static T TryParse<T>(this string value, ParseDelegate<T> parse) where T : struct
   4:  {
   5:      T result;
   6:      parse(value, out result);
   7:      return result;
   8:  }

This allows our TryParseInt32() implementation to now be a single line of code:

   1:  public static int TryParseInt32(this string value)
   2:  {
   3:      return TryParse<int>(value, int.TryParse);
   4:  }

The ParseDelegate<T> is generic that matches the signature of all the TryParse() methods. So we can imply pass in the int.TryParse delegate and it will be executed *inside* the re-useable TryParse<T> method that I have created.  This utility class can be used in all kinds of different string parsing situations.  Here is the complete code for the TryParseExtensions class:

   1:  public static class TryParseExtensions
   2:  {
   3:     
   4:      public static int TryParseInt32(this string value)
   5:      {
   6:          return TryParse<int>(value, int.TryParse);
   7:      }
   8:   
   9:      public static Int16 TryParseInt16(this string value)
  10:      {
  11:          return TryParse<Int16>(value, Int16.TryParse);
  12:      }
  13:   
  14:      public static Int64 TryParseInt64(this string value)
  15:      {
  16:          return TryParse<Int64>(value, Int64.TryParse);
  17:      }
  18:   
  19:      public static byte TryParseByte(this string value)
  20:      {
  21:          return TryParse<byte>(value, byte.TryParse);
  22:      }
  23:   
  24:      public static bool TryParseBoolean(this string value)
  25:      {
  26:          return TryParse<bool>(value, bool.TryParse);
  27:      }
  28:   
  29:      public static Single TryParseSingle(this string value)
  30:      {
  31:          return TryParse<Single>(value, Single.TryParse);
  32:      }
  33:   
  34:      public static Double TryParseDoube(this string value)
  35:      {
  36:          return TryParse<Double>(value, Double.TryParse);
  37:      }
  38:   
  39:      public static Decimal TryParseDecimal(this string value)
  40:      {
  41:          return TryParse<Decimal>(value, Decimal.TryParse);
  42:      }
  43:   
  44:      public static DateTime TryParseDateTime(this string value)
  45:      {
  46:          return TryParse<DateTime>(value, DateTime.TryParse);
  47:      }
  48:   
  49:      #region Private Members
  50:   
  51:      private delegate bool ParseDelegate<T>(string s, out T result);
  52:   
  53:      private static T TryParse<T>(this string value, ParseDelegate<T> parse) where T : struct
  54:      {
  55:          T result;
  56:          parse(value, out result);
  57:          return result;
  58:      }
  59:   
  60:      #endregion
  61:   
  62:  }
posted on Sunday, October 05, 2008 5:30 PM

Feedback

# re: TryParse Extension Methods 10/5/2008 3:37 PM JeroenH
I firmly disagree with this approach. Since you're not using the boolean return value from the TryParse method, I guess you're assuming that the parsing will always succeed. In other words, if the input string is NOT a valid (int, double, ...), an exception can be thrown. So why not simply use the Parse() variant then?

If the input can be invalid (e.g. because it's user input), and you want to test it for correctness, you do need the return value from TryParse(), in order to handle the incorrect input string appropriately...

# re: TryParse Extension Methods 10/5/2008 3:46 PM Steve
Yep, that is a totally valid point and I should have pointed out that disclaimer. The big caveat is that IF it is not valid then it will gracefully be set to a default value. This *may* or *may not* be desirable based on what you're trying to do. It could even be extended to allow a default value to be passed in similar to how the GetValueOrDefault() method works for nullable types.

Here is a scenario where this could have value. Let's say the use is posting from a ViewPage in MVC from a textbox that needs a number but the user typed letters. Using this would set the value to 0 since the parse would not work. The validation on that property would have to say that 0 was not valid. The original "attempted value" could still be passed in just fine.

Again, this is just an option and not a one-size fits all solution.

# re: TryParse Extension Methods 10/6/2008 12:03 AM Divya Jain
Very good website. I liked it very much. Comments from http://www.valentineday.in

# re: TryParse Extension Methods 10/7/2008 4:24 PM BEM
Good post. For comparison, below are a couple of generic TryParse extension methods. They'll each TryParse about anything that's TryParsable.

This one uses reflection and is about 50X slower than your methods.
Usage: var x = "3.1415".TryParse<float>();

public static T TryParse<T>(this string value)
{
Type t = typeof(T);
T type = default(T);

// Look for the TryParse method on the type.
System.Reflection.MethodInfo tryParse = t.GetMethod("TryParse", new Type[] { typeof(string), Type.GetType(t.FullName + "&") });
if (tryParse == null)
throw new NotSupportedException("TryParse is not supported on type " + t.FullName);
else
{
// Try parse exists. Call it.
Object[] ps = new Object[2];
ps[0] = value;
tryParse.Invoke(type, ps);
return (T)ps[1];
}
}


This one is about 10x slower and returns null when parsing fails.
Usage: var x = "3.1415".TryParse<float>();

public static T? TryParse2<T>(this string value) where T : struct
{
if (value == null) return null;

T? result = null;
var converter = TypeDescriptor.GetConverter(typeof(T));
if (converter != null)
{
try
{
result = (T)converter.ConvertFromString(value);
}
catch { }
}
return result;
}

# re: TryParse Extension Methods 10/16/2008 3:22 PM Jay Kay
I'm going to have to agree with Brad and the other dude. What is it you hoped to accomplish here? You have failed miserably.

# re: TryParse Extension Methods 11/5/2008 9:24 PM Asim Sajjad
Yes i am also agree with the other guys , what happen if the input is not value?

# re: TryParse Extension Methods 11/10/2008 12:06 PM Dana Hanna
I much prefer my approach:

http://thesoftwarejedi.blogspot.com/2008/05/extension-methods.html

:)

Post Feedback

Title:
Name:
Email: (never displayed)
Url:
Comments: 
Please add 5 and 5 and type the answer here: