Myles
Myles

Reputation: 683

Why is TryParse the way round that it is?

I've been struggling to get my head around a natural way of using TryParse because I keep expecting it to work the other way around (i.e. to return the parsed value and emit the boolean for whether the input parsed).

For example, if we take a basic implementation of Parse, the return value is the parsed input:

int parsedValue = int.Parse(input);

This works fine until it gets a value that it can't parse, at which point it entirely reasonably throws an exception. To me, the option then is either to wrap the parse in something like a try-catch block to handle the exception and a condition to set a default, or just to use TryParse to let C# do all that for me. Except that's not how TryParse works. The above example now looks like this:

bool parseSucceeded = int.TryParse(input, out int parsedValue);

To get it to assign in the same way as Parse, I wrap it in a ternary conditional with parsedValue and a default value (in this case, 0) as the true and false results respectively:

int parsedValue = int.TryParse(input, out parsedValue) ? parsedValue : 0;

But I still feel like I'm missing the point with TryParse if I'm just working around its default behaviour like this. I've read Tim Schmelter's excellent answer in which he shows its internal workings, from which I can suppose that it returns the boolean because it's easier internally than passing it out at all the various places that it currently returns. But I'm not sure about this, and I'm not satisfied that I understand its intent correctly. I also tried reading the documentation for it, but the remarks don't clear up my confusion (I don't think they even make its differences with Parse clear enough, like the change in return type).

Am I understanding it correctly, or am I missing something?

Upvotes: 1

Views: 420

Answers (3)

Austin T French
Austin T French

Reputation: 5131

I think, a large part of your confusion stems from the method name isn't named exactly right:

int parsedValue = int.Parse("42");

This makes perfect sense, give me the integeger representation of a string.

int parsedValue = int.TryParse(input);

This makes sense as an extension of the concept: Input might be '42' or 'Oswald', but if it's a number I want that number.

In 2020, I think a better name would be CanParse(string input, out int result).

  1. It better matches style guides and naming conventions, where returning a bool should be named with Is, Has, or Can.
  2. It better matches how we use TryParse 99% of the time:

    if (int.CanParse(input, out int result)) { return result * 10; }

But where I feel the current name makes sense, is the problem I assume it was trying to solve: To get rid of the following boilerplate code:

int result;
bool hasValidNumber = false;

try
{
    result = int.Parse(input);
    hasValidNumber = true;
}
catch
{
    // swallow this exception
}
if (hasValidNumber)
{
   // do things with result
}
else
{
   // use a default or other logic
}

Upvotes: 1

D Stanley
D Stanley

Reputation: 152566

Sure, it could have been implemented as

int TryParse(string input, out bool succeeded)
{

}

But as mentioned in a comment, the common use case for the function is:

string input;
int parsedValue;
if(int.TryParse(input, out parsedValue))
{
    // use parsedValue here
}

With the signature you propose, that code would now be:

string input;
bool succeeded;
int parsedValue = int.TryParse(input, out succeeded)
if(succeeded)
{
    // use parsedValue here
}

So there's more code for no functional benefit. Also, with your ternary operator, if the parse fails you just set a value of zero, which is unnecessary since the default value of it is 0. You could just do:

int parsedValue; 
int.TryParse(input, out parsedValue);

If the parse fails, parsedValue will have a value of 0; (I also question if/how you distinguish between an actual result of 0 and a failed parse, but I'm sure you have a reason).

So there's no technical reason why the signature is the way it is; it's a design decision that is appropriate for the most common use cases.

Of course, now with tuples in C# 7 you could have:

(int parsedValue, bool succeeded) = int.TryParse(input);

but again there's little functional benefit and prevents you from inlining the TryParse in an if statement.

Upvotes: 3

Johnathan Barclay
Johnathan Barclay

Reputation: 20363

Because logically you would want to check that the TryParse succeeded before trying to use the out value.

So this is more concise:

if (int.TryParse(input, out int parsedValue)}
{
    // Do something with parsedValue
}

Than this:

int parsedValue = int.TryParse(input, out bool succeded);
if (succeeded)
{
    // Do something with parsedValue
}

Upvotes: 2

Related Questions