Question

Given that the .NET team has stated (I'll find a source...) that they regret the design of the primitive type's parsing methods (e.g. Int32.TryParse(String s, out Int32 result)) why haven't these been updated with a more obvious and client-friendly variant?

Framework Version:

Int32? numValue;
Int32 placeHolder;
if (! Int32.TryParse("not a number", out placeHolder))
    numValue = 10;

Improved Version:

var numValue = Int32.Parse("not a number", 10);

Where the signature for the improved parsing method is:

public static Int32? Parse(String s, Int32? defaultValue = null);

And it might have a naive implementation of:

public static Int32? Parse(String s, Int32? defaultValue = null) {
    Int32 temp;
    return ! Int32.TryParse(s, out temp)
        ? defaultValue
        : temp;
}
Was it helpful?

Solution

I'm not sure there would be a definitive answer to this unless someone from the BCL team has a story to tell.

Some considerations might be:

  1. Changing the methods would constitute a large breaking change to existing code bases with very little (debatable if any) gain. Some might argue that it would be a detriment for the reasons below.
  2. Nullable types box the wrapped wrap the underlying value causing a (minor) performance hit always even when you expect your parse to succeed and don't want a nullable type anyway.
  3. If you know your input is supposed to be valid, then the Parse method throws exceptions for the exceptional case that the input is invalid. This is generally more performant as we don't have extra error handling code for when we don't need it, and expected from a design point of view. If you plan on handling the invalid case, that's what TryParse is for.
  4. Confusion between int Parse(string, int? defaultValue) and int Parse(string), mostly with regards to their error handling. Notice I've remove the optional part of the first method otherwise it would make no sense to have both methods (you would never be able to call your method without explicitly passing in null). At this point, it would be a bit confusing as one overload throws an exception on failure whereas the other one does not. There are a few exceptions to this, such as Type.GetType but they're generally rare and in such cases they make use of an explicitly named parameter indicating that it will or will not throw an exception.
  5. Another benefit of TryParse as designed is that it is pretty explicit by way of its API for handling the pass/fail result. Rather than a magic number/result (null) indicating a failure, it returns a separate true/false value. Now, some mechanisms do this (String.IndexOf for example returning -1) so it's debatable if this is a good thing or a bad thing. Depending on your practice, using the return true/false value might be easier, or using the magic null result might be. But they decided I guess to not muddy the waters with two methods doing the exact same thing but with slightly different signatures/return values.
  6. Another consideration is how common nullable value types are. Are you really using Int32? in many places? Or is it just for error/input handling? In my experience, nullable values are mostly used for database I/O (and even then, not that often). The only other times could be for input from non-trusted sources which would only be for the initial interfacing; all underlying code would still be typed against the non-nullable Int32. In such case you have two methods, the original:

    int input;
    if (TryParse(someString, out input))
        DoSomethingValid(input); //valid! Do something
    else
        ErrorMessage()//not valid, error!
    

    Or your suggestion:

    int? input = Parse(someString)
    if (input != null)
        DoSomethingValid(input.GetValueOrDefault())//valid! Do something
    else
        ErrorMessage()//not valid, error!
    

    Note how similar both are. Your suggestion really provides very little, if anything to this case. Also note the usage of GetValueOrDefault(), this is actually the faster/better way to access the wrapped value if you know it's non-null, but rarely (at least from what I see on the internet) is it used over the Value accessor. If the BCL added/changed the Parse method as you suggest, I think a lot of people would be unnecessarily hitting the Value accessor.

    I can't comment specifically on cases using nullable values significantly throughout an application design and/or its layers, but in my experience they are rarely used or should be rarely used (to me, it's a code smell almost on the level of "stringly-typed" data)

Ultimately, I believe part of the reason why extension methods were added (aside from Linq) was to allow users to roll their own API as needed. In your case, you can easily add an extension method to obtain the syntax you want:

public static Int32? Parse(this String s, Int32? defaultValue = null)
{
    Int32 temp;
    return !Int32.TryParse(s, out temp)
        ? defaultValue
        : temp;
}

string validString = "1";
int? parsed = validString.Parse(); //1
int? failParsed = "asdf".Parse(9); //9

The teams generally favour maintaining the status quo and additions have to provide a significant enough benefit to be added to the system, and the team also considers what workarounds already exist in the API as-is. I'd suggest that given the extension method option, it's not that big of a concern to change the API considering the signficant breaking nature of it.

OTHER TIPS

This signature:

public static Int32? Parse(String s, Int32? defaultValue = null);

would conflict with the existing Parse(string s) overload, because there is no way to tell them apart if the optional argument is not specified. The existing methods will probably never be changed or removed, because it would break compatibility with existing code.

However it would be possible to create overloads of TryParse that don't take a out int value parameter:

public static int? TryParse(string s)

Note that you don't need to add a parameter for the default value: you can use the null coalescing operator instead.

string s = ...
int value = int.TryParse(s) ?? 0;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top