krimog
krimog

Reputation: 1276

Why doesn't T.TryParse return a Nullable<T>

As far as I know, int.TryParse(string, out int) exists since Framework 2.0. So does int?.

Is there a reason to use an out parameter instead of returning an int? with HasValue set to true of false depending on the ability to convert ?

Upvotes: 19

Views: 1438

Answers (5)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149538

Here's a quote from Julie Lerman's blog (Back from 2004):

I have played with nullable in the March preview bits, but not yet in the May and disappointed with the current (but slated for serious improvement by the bcl team!!!) performance when I compared the using nullable<t> over current options. So for example with value types:

comparing myNullableInt.HasValue to (in VB) is myInt < 0

or with reference types

comparing myNullableThing.HasValue to “if not myThing=null

the nullable type is currently much much slower. I have been promised by a few on the BCL team that the plan is to make the nullable MUCH more performant.

I have also been given the hint that in the future, the following will be possible:

Nullable<T> Parse(string value); 
Nullable<Int32> i = Int32.Parse( some String );

And will be more performant than TryParse. So that, too will be interesting.

I assume that as always, the benefit outweighs the cost.

Anyway, in the upcoming C# vNext, you can do:

DateTime.TryParse(s, out var parsedDateTime);

Turning TryParse into a one liner.

Upvotes: 5

O. R. Mapper
O. R. Mapper

Reputation: 20722

I cannot tell about the actual reasons, but I see three possible reasons:

1) Nullable types were introduced in .NET 2.0, while the first TryParse methods were already around since .NET 1.1. Thus, when nullable types were introduced, it was too late for such an API change; and new classes wouldn't implement TryParse differently because the pattern had already been set.

2) Not all types can be used with the Nullable structure, only value types can. However, there are methods following the Try* pattern that have to return reference types. For example, a dictionary may totally legitimately contain null as an item, hence its TryGetValue method needs an additional way to express that a key was not found.

3) The way the Try*-methods are written, it is possible to write code like this:

int myValue;
if (int.TryParse("42", out myValue)) {
    // do something with myValue
}
    // do something else
}

Now, imagine if TryParse only returned an int?. You can either dispose of the myValue variable and lose the result:

if (int.TryParse("42").HasValue) {
    // do something with ... what? You didn't store the conversion result!
}
    // do something else
}

Or you can add a nullable variable:

int? myValue = int.TryParse("42");
if (myValue.HasValue) {
    // do something with myValue.Value
}
    // do something else
}

This isn't an advantage over the current version any more, and instead it requires writing myValue.Value at some later instances, where otherwise a simple value would have sufficed. Note that in many cases, you only need the information about whether the operation was successful for the if statement.

Upvotes: 21

Marc Gravell
Marc Gravell

Reputation: 1062560

One other possible reason:

Generics for .NET and C# in their current form almost didn't happen: it was a very close call, and the feature almost didn't make the cut for Whidbey (Visual Studio 2005). Features such as running CLR code on the database were given higher priority.

...

Ultimately, an erasure model of generics would have been adopted, as for Java, since the CLR team would never have pursued a in-the-VM generics design without external help.

source: http://blogs.msdn.com/b/dsyme/archive/2011/03/15/net-c-generics-history-some-photos-from-feb-1999.aspx

My point being: the majority of changes in the BCL (or at least those not directly related to generics) probably needed to work both with and without generics, in case that feature was cut in the final RTM.

Of course, this also makes sense from a calling client perspective: all the consuming languages (ok, there weren't as many back then) would ideally have been able to use them - and out parameters weren't as cutting-edge as generics.

Upvotes: 4

anaximander
anaximander

Reputation: 7140

The simple reason is because when int.TryParse was added to the language, Nullable<T> didn't exist.

In this blog post by Eric Lippert, there's a line towards the bottom that reads:

The solution is to write your own extension method version of TryParse the way it would have been written had there been nullable value types available in the first place

which makes it clear that nullable types were not available to be used in the original implementation of TryParse. Eric Lippert was on the team that wrote the C# compiler, so I'd say that's a pretty authoritative source.

Upvotes: 24

Martijn
Martijn

Reputation: 12092

As to reasons we can only guess, but some possible reasons are:

Assignment overhead: a boxed value incurs some (small) performance overhead over a built in type.

No real gains:

int res;
if int.TryParse("one", out res) {
  //something
}

isn't much worse than

int? res = int.TryParse("one");
if (res.HasValue){
  int realres = res.Value
  //something
}

Upvotes: 3

Related Questions