I'm wondering if there is a name for this "pattern" where a method signature is called TrySomething, e.g. int.TryParse
, decimal.TryParse
, etc.
A coworker of mine uses this naming convention frequently -- rather than returning a value or throwing an exception, they will call a method TryDoSomething
and if an exception is caught during processing it gets returned via out param开发者_开发问答.
Edit: I understand the example privided is not how the TryParse methods work. That was the point of posting this... I wasn't sure what to call it. I agree it seems more like a naming convention and not a pattern. Thanks for all the input.
Edit: Interesting...
Consider the TryParse pattern for members that may throw exceptions in common scenarios to avoid performance problems related to exceptions.
To implement The TryParse pattern, you provide two different methods for performing an operation that can throw exceptions in common scenarios. The first method, X, does the operation and throws the exception when appropriate. The second method, TryX, does not throw the exception, but instead returns a Boolean value indicating success or failure. Any data returned by a successful call to TryX is returned using an out (ByRef in Visual Basic) parameter. The Parse and TryParse methods are examples of this pattern.
I would likely call that the Error Hiding Pattern. You benefit from creating a TryX
when you have code that would usually produce an exception that instead cuts out early with a boolean. If you look at the methods provided by the framework you'll notice the TryX
variants exist whenever it would be non-trivial or error-prone (or done so often it should be in the framework) to write your own IsValidX
method.
If you have to catch an exception, there is no sense wrapping this in a method. All you are doing is making it harder to debug input problems. Instead of having a nice stack trace to follow to trace down an input failure, the user may only see side effects of the failed method. Worse still, when debugging the issue at hand the developer potentially has to recreate obtuse program state in order to effect the failure mode.
Basically, if a priori you know an operation will fail with an exception, it is logical and proper to provide a companion TryX
operation. This is the so-called Tester-Doer pattern. Post hoc analysis of an operation is not Tester-Doer, but rather just simple Exception Handling.
In this post about TryParse they call is the Tester-Doer pattern.
First of all, if your description is accurate, that's not how int.TryParse and its siblings work at all.
I'll admit, those methods are slightly broken, in my opinion, since they don't convey the reason for the parsing failure to the caller, only that it failed. In this respect, I'd love to see a better way to handle this.
In fact, a better way, again in my opinion, was something I saw in a third party library which I don't remember, but basically they had various custom types which had such a Parse/TryParse pair of methods, and they did the following:
- Define a generic struct which contained both the value obtained by parsing the string as well as a value of an enum type conveying the result of parsing
- All the TryParse methods returned this struct, and had no
out
parameter - The Parse methods simply called the TryParse methods, and then translated the various non-success results of that enum into appropriate exceptions
Again, the main problem here, in my opinion, was that it wasn't extensible. If I wanted to use their system, and types, and add a reason of my own, I couldn't do that, but some variants of that would easily handle this.
In any case, the int.TryParse methods doesn't internally throw any exceptions at all. Instead they go through the motions of actually parsing the string, and if they encounter something they can't cope with, they simply return false, that's it, no exceptions in play.
Exception handling is slightly more expensive than the alternative without exceptions, which is why some such core methods are optimized for performance.
That's why in my comment I called your pattern, if accurate, stupid, because you have combined exception handling with the out parameters. And what can you do with that exception once you've retrieved it? throw it? Then you're back at square one.
I would seriously look at your pattern there and try to change it.
Of course, all this hinges on the assumption that your description of it is accurate.
You could call it a ExceptionSafeBridge
. It leads you from the exception world to the error-code world. But I don't think it's official.
This pattern is common when you need to bridge between e.g. C code and managed code.
I call it the "Wish I had Option<T>
" Pattern (which is similar to the "Wish I had Either<T,E>
" Pattern -- imagine where E : Exception).
TryXYZ (in the above examples) simulates Option with using a boolean result and out
paramter. (For value types it could be Nullable<T>
in this case -- I suspect it is not like this for int.TryParse
and friends partially because Nullable's came much later to .NET). The returning the exception via out
more closely resembles Either.
In C# I would not recommend catching exceptions just to pass them to out
parameters in general though (this "rule" may be different with a language that has support for discriminated unions and pattern matching) -- I try to 1) deal with it "correctly", however that is defined, which very well may be out
in particular circumstances; or 2) let it go so the caller can try the same.
(As others have pointed out, this is more of a convention).
Happy coding.
A design patters in usually referred to a general idea which is language-agnostic, so no, this isn't a pattern. It may be an idiom, but most likely it's just a naming convention, as Aaron rightly mentioned in a comment.
Yep. It's called the Tester-Doer pattern.
if an issue occurs during tryparse i.e.
int val;
if(int.TryParse("2", out val))
{
//do work with val
}
Then you wouldnt have an exception caught via out param, value 0 for out, false returned as the boolean value.
Best bet is to use "is" or "as" instead.
I wouldn't really call the approach you describe a pattern though... Just a coding practice (not the best one).
Example of as:
private void SetObj(object obj)
{
int thisInt = obj as int;
if(thisInt != null)
{
//do work
}
else
{
//handle issue
}
}
The above is much more efficient at runtime than try / catch.
If you use "is" when as is not available to you ("as" not usable on all types), be sure not to add redundancy by implementing as WITH is... only make use of one or the other.
精彩评论