I wouldn't want that at all. If I'm calling CanParse(), 99 times out of a
100 it's because I want to parse the string. It'd be silly to call a
method that basically has to do the work of parsing the string, but which
only returns a flag telling me if actually parsing the string will work.
Well thats the crux of the matter isn't it? Why is it "silly" ?
Sometimes as experienced developers we can start thinking like a computer
and write code that is constantly performance 'aware' even when its not
necessary which is _most_ of the time.
If anything the "Try..." pattern is a great example of the usefulness of
by-reference parameters.
Its also an example of obscurity of function. The vast majority of
functions we use will take input only parameters, the paremeters are never
changed by the function and the result of the function is its return value.
Anything which varies from that makes code harder to read. True TryParse
has its uses where performance is needed but the CanParse pattern I
suggested is more _human_. Can I parse, ok so I will. Simple clear
obvious.
You write "forced" as though there's something about calling TryParse()
that is harmful.
No I wrote "Not having CanParse I'm forced ...". Its not the presence of
TryParse as an option which bothers me its not having a CanParse that I have
issue with.
Beyond that, however, since sometimes performance _is_ an issue, it's not
like TryParse() could have been left out of the API.
Agreed and I didn't suggest that should've.
Why is it the case that "how did x get assigned a value" is "not that
obvious"? The "out" in the parameter list for the call to TryParse()
makes it very clear where x is getting assigned.
Its a relative thing. True it isn't actually hard to see that the out
keyword on the parameter indicates where x gets its value. However it is
harder to see than x = because that's what our minds will be condition to
expect when looking for an assignment. This may seem to be a microscopic
and insigniifcant difference but to think so is a mistake.
Think of it this way. The difference in real time between CanParse/Parse
and TryParse will be measured in nano-seconds. However its a difference
which you seem to think is worth gaining. In a few case (and let me
re-iterate it, it _will_ be a few) this may actually be true.
The same principle can be applied to readability of code. Small differences
such as CanParse/Parse vs TryParse only have a small effect individually on
readability however the cumalitive effect of computer oriented over human
oriented coding is significant.
The big difference between the two is that only a small amount of the
overall number of lines actually impact in any signficant way the perfomance
of most apps whereas all lines of code affect readability.
Especially in the context of C# where the compiler is very strict about
dealing with uninitialized variables, I never find myself wondering how
something got assigned. Because an "out" parameter is required by the
compiler to be assigned in the method being called, a call like this is no
exception.
Thats great if you happen to think like the C# compilier, perhaps you do and
you see out parameters as easily as assignments. However most humans would
not. So as long as no other less experienced developers have to come along
and read your code that'll be fine.
If performance is not a concern (and you already wrote that it almost
never is), you can always just wrap a call to Parse() with a try/catch
block. That provides the exact assignment pattern you're asking for.
That simply replaces one unusual pattern of code with another. Not a
solution to the human problem I'm looking for.
I would not be against the inclusion of a method like that as an
_optional_ pattern. However, I certainly would resent being forced to use
it in situations where I don't already have a nullable type.
Agreed. Changing the type of a variable to accommodate this would be a bad
thing. However there are some cases where in fact the nullable type is the
appropriate type. It would be nice to have the choice of all three but if I
could only have two I would take CanParse and TryParse. Sadly I only have
the one TryParse.
Indeed. And IMHO it's the principle that's flawed. There's nothing
inherently wrong with passing by reference. Why go out of your way to
avoid it?
My comments above outline why you should. Apart from being correct the most
important thing for code to be is readable not just to yourself but any who
may need to. Code that is readable to a human is not always code that uses
the full cleverness of computer science but takes into account the
psychology of the human reading it.
ByRef parameters are unusual and can catch the less wary out. They can be
gotchas even for the experienced. Whilst they do have their place they
should be used sparingly and be treated with suspicion. In short, they
smell bad.