1

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?

Myles
  • 543
  • 8
  • 13
  • 7
    The most common use pattern is `if (int.TryParse(...)) { ... }`. – GSerg Jun 19 '20 at 13:46
  • 2
    The `bool` value is always an actual value as in it will always either mean the value parsed sucessfully or not. On the other hand the "value" is not always an actual value because when it fails to parse it's just the default so if you just took that value and forgot to look at the `bool` then you might think it parsed to the default value when it did not parse at all. – juharr Jun 19 '20 at 13:49
  • 1
    Also if you are ok with `0` for default you don't need to use ternary operator `TryParse` will set `parsedValue` to `0`. – Guru Stron Jun 19 '20 at 13:50
  • 2
    It's also likely that this would have been designed to return a value tuple `(bool, int)` if those had existed from the start, but they didn't and they are not going to change it now as that would be a breaking change. – juharr Jun 19 '20 at 13:52

3 Answers3

3

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.

D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • Thank you for explaining it so clearly (and your tuple suggestion - I suspect that will come in very handy). It definitely makes more sense now. – Myles Jun 19 '20 at 14:53
  • With regards to not distinguishing actual zeroes from a failed parse, the particular use case I'm currently working on is display logic for a form - the 0 and `parsedValue` is part of the workaround for assignment, as I want it to update a displayed variable back to a default value when invalid input is entered (otherwise I've found it sticking on the last valid input, and I want it to be visually clearer that something is missing before I throw a prompt). I am also using the sort of `if` block you describe, but it's elsewhere in validation logic to catch before the data goes anywhere. – Myles Jun 19 '20 at 15:02
2

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
}
Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35
1

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
}
Austin T French
  • 5,022
  • 1
  • 22
  • 40
  • What you say about the name makes sense - I think it is the fact that it seems to be describing the parsability of the input, but I understand that the way that I'm using it (i.e. agnostic of the value's parsability) is rather unusual and not normally good practice. – Myles Jun 19 '20 at 15:10
  • 1
    I think we have all had a scenario where we just wanted the number, without throwing exceptions. Your own extension method might be better, where you can be verbose with your intent and not sprinkle this odd looking code everywhere => https://dotnetfiddle.net/uyzoFc – Austin T French Jun 19 '20 at 15:28
  • That's basically what I'd done at one point, but then undid from a load of places because it just felt weird (I still have it for now as a private method that a constructor uses for some setters, but at least scaled it back from being a public method that another class enviously used). – Myles Jun 19 '20 at 16:00