|
The right tool for the job are option types[^]. Unfortunatelly, most mainstream programming languages do not support them directly. In C++, there is the Boost Optional Library[^] that servers this purpose.
We have discussed this topic in the Lounge[^] not so long ago
|
|
|
|
|
Internally at CodeProject we've implemented something very similar using generics, though our use is still a little sporadic since it was a late-comer to the game. We extended the concept to not only include the value and whether or not the value is there, but also error information in case an error was the reason a no-show occurred.
I still check for nulls though!
cheers,
Chris Maunder
The Code Project Co-founder
Microsoft C++ MVP
|
|
|
|
|
Chris Maunder wrote: I still check for nulls though!
Yes, simply having option types available is not enough. To be foolproof in this regard, a language/environment must both support option types and not support nulls. I think Haskell does it correctly, but there are not many Haskell jobs out there
|
|
|
|
|
I have made myself a Nullable<> template, that I have used with great success. It works a lot like what the Wikipedia-article explains.
--
Kein Mitleid Für Die Mehrheit
|
|
|
|
|
So here's where I stand:
1. If I'm asking you to look up an object or return a list of objects matching some criteria then I do not expect an exception to be thrown. Not finding something, or having an empty list is not exceptional. Having a database fail isn't exceptional. Having all code always work perfectly with butterflies and roses is exceptional. Exceptions, in my book, are to be used to catch exceptional cases, not to control program execution flow.
2. I don't care what you return from a method. Return null. Return a default value. Return a pink giraffe. I will check your return value to ensure it's non-null and to ensure it's a sensible value. I will do this everytime at every point because I do not trust code. I don't trust the code someone else has written. I don't trust the code I wrote 2 years ago. What if we're using different conventions? What if I had too much sugar two years ago and decided I should return a valid object, everytime, always, then in deep, dark Toronto Winter I decided "screw it: you're getting a null".
So the convention I use is: use a common sense convention and be consistent about it but at the end of the day, when everyone's had their fun, I'm going to double check whatever it is you gave me because I'm crabby and cynical and computers hate segfaults[^].
cheers,
Chris Maunder
The Code Project Co-founder
Microsoft C++ MVP
|
|
|
|
|
I am surprised anyone answered anything other than "Other". Because surely there's no single answer to this that can work for all scenarios. You may have to return null, an empty object, an error code, an indication code, or even throw an exception. (sometimes even a combination of exception/return value).
|
|
|
|
|
|
Returning null is problematic unless it's documented what returning null means. When consuming Microsoft's classes this is usually the case. In a typical "Find" algorithm it's quite usual for a negative result to be valid behaviour. Returning a null object is perfectly fine in this case. However, I've often maintained code where, due to lack of documentation, it's not clear whether returning null from the method you were consuming was possibly valid behaviour or not.
The upshot is that for my own methods, in those cases where I may return null, I document what this means. Design by Contract in .NET 4.0+ will also be useful going forward if developers start using it.
Kevin
|
|
|
|
|
Agreed, there are many scenarios where different approaches are needed.
If my GetConnectionString() method can't find the connection string (maybe somebody fooled around with the config files), I would almost certainly throw an exception. On the other hand, if my GetTradeData() method can't find trade "T12709", I would almost certainly return null.
Cheers,
Vikram. (Proud to have finally cracked a CCC!)
|
|
|
|
|
Nish, I agree with your point as well but the question is about returning "no result" so, I prefer to return "null"!
Try not to become a man of success but rather to become a man of value. - Albert Einstein
Ernest Laurentin
|
|
|
|
|
Chris Maunder wrote: Exceptions, in my book, are to be used to catch exceptional cases, not to control program execution flow.
I agree[^], although there are people who think it is acceptable to use exceptions for the tasks such as breaking from deeply nested loops or ending a numerical computation.
But in general, not finding a value in a collection is not an exceptional event. STL containers usually return the "past-end" element in this case, but an option type object would be even better.
|
|
|
|
|
Nemanja Trifunovic wrote: But in general, not finding a value in a collection is not an exceptional event
I like to think of it in "design by contract" terms. If a method cannot fulfil its contract then it must cause an exception (or generically, a program fault) otherwise it should not.
I think the "exceptions are for exceptional cases" mantra is too vague. We might envisage a method where a rare event occurs but the method's spec is designed to account for that. In that case it should not cause an exception, even though the event was "exceptional."
You could put it this way: "Exceptions imply exceptional cases (because errors are exceptional) but not vice-versa."
Kevin
|
|
|
|
|
Kevin McFarlane wrote: I like to think of it in "design by contract" terms.
Can I smell Eiffel here ?
Seriously, you are right - we just have a terminology problem here: by "exceptional" I do not mean "rare" but "unexpected" or "violated the contract".
modified on Monday, August 24, 2009 10:48 AM
|
|
|
|
|
Nemanja Trifunovic wrote: Can I smell Eiffel here Smile ?
Yeah, or Spec# or Code Contracts for .NET 4. Not sure if there's anything which supplies full support in C++ though?
Kevin
|
|
|
|
|
Kevin McFarlane wrote: Not sure if there's anything which supplies full support in C++ though
Nothing built-in, but there are libraries - in fact I remember one published here at CP.
There are some non-standard extensions for DbC in the Digital Mars C++ compiler, and of course D supports it (at least D 1.0, not sure about 2.0)
|
|
|
|
|
I agree with Chris. That is the way to do it!
Throwing an exception is unacceptable since exception should be used for errors. "No result" is not (always) an error condition.
Morale: Use the right tool for the right task.
Bye By(t)e
|
|
|
|
|
Returning a list is different from returning an object. If my requirements are to return a list, I always return a list (albeit it may be empty). But if I am supposed to return an instance of some one object I usually will return null if I cannot create a valid object hence my vote for null.
WarePhreak
Programmers are tools to convert caffiene to code.
|
|
|
|
|
Yeeesss... but an Exception can tell you why you got nothing useful back.
"Waiter! I can't eat this soup." ...
|
|
|
|
|
If the object is expected then returning a null will force the caller to check the return type and then try to work out why the call failed.
If you think the object might not be found then you should either provide a method to check the object exists or implement a Try... method that returns a boolean success/fail and the object as an out parameter.
|
|
|
|
|
Checking the what a function return is always a good idea: Using exceptions and a try/catch just to avoid check the result is only lazyness (IMO).
However I agree in general that you should provide a way to quick check the result of the operation result i.e. supposing you wrote a function that search one element in a collection I prefer something like the following:
// Search an object and return its position (if found) using pPositionWhereFound.
Bool searchObj ( Oject_t *pList, Oject_t* pObjToSearch, int *pPositionWhereFound );
instead of:
// Search an object and return its position (if found). -1 as return value
// means not found
int searchObj ( Oject_t *pList, Oject_t* pObjToSearch );
in both cases you have to check the returned value but doing:
if ( searchObj( &List, &ObjToSearch, &Position ) )
...
is nicer than:
int iPosition = searchObj( &List, &ObjToSearch );
if ( iPosition != -1 )
...
Bye By(t)e
|
|
|
|
|
Simone Serponi wrote: Checking the what a function return is always a good idea
Yes, but hope that consumers of your code will always check if you return null or valid object is just asking for trouble.
To be honest I prefer bug report with exception message "check_first_if_object_exeists_before_requesting_it_noob" than plain null object exception. It just saves time fixing code (on any side)
--
"My software never has bugs. It just develops random features."
|
|
|
|
|
deflinek wrote: Yes, but hope that consumers of your code will always check if you return null or valid object is just asking for trouble.
To be honest I prefer bug report with exception message "check_first_if_object_exeists_before_requesting_it_noob" than plain null object exception. It just saves time fixing code (on any side) Cool
As I already told checking function return value is (should be) a must...
Moreover this is the classical thing that is documented into the Reference Manual of the code (thanks to God for DoxyGen) and looking at the documentation before use it is always a good idea.
However, yes, "The mother of noobs is always pregnant", and I'm sure someone will use the function without check the return value. But in this case it is not my fault: Read the documentation first!
Bye By(t)e
|
|
|
|
|
Member 2053006 wrote: If you think the object might not be found then you should either provide a method to check the object exists or implement a Try... method that returns a boolean success/fail and the object as an out parameter.
Totally agree. I'm a big fan of the TryParse() style methods.
There are some cases where they don't apply so well; e.g., cases where the condition can change between the check and use. A check for a file's existence doesn't mean that the file is still there when you try to open it in the next statement. Sometimes I think TryParse() style methods in these cases can create a false sense of security... OTOH, at least they let you return an intelligible error message for many execution scenarios.
|
|
|
|
|
With the current wording of the question, I throw. An error always gives an exception in my book. However, I don't always consider something missing as an error, only if I expect it to be there. I usually do this:
Foo bar = provider.GetFoo(FooType.Bar);
Foo baz = provider.FindFoo(FooType.Baz);
--
Time you enjoy wasting is not wasted time - Bertrand Russel
|
|
|
|
|
I agree about the "Find", but don't necessarily agree on the "Get". Please see my reply to Nishant above, if my GetTradeData(string tradeID) fails, I would not throw an exception.
FindTradeData(string tradeID) sounds silly.
Cheers,
Vikram. (Proud to have finally cracked a CCC!)
|
|
|
|