16

I was wondering how to decide between :

1) If to throw custom exceptions OR

2) return a kind of LOG object that has flags like 'CityNotFound,' 'ReferenceConstraintBroken' etc.

I have been reading the exceptions are expensive. If I just need to know specific details of a process result, to me it sounds more beneficial to have a custom 'process LOG object' that contains only the necessery information of a process.

So, if I come back to my question:

When is better to throw an exception and when is better to return some error log 'object' ?

pencilCake
  • 51,323
  • 85
  • 226
  • 363
  • 2
    Does anybody remember langauges without structured exception handling? Those LOG objects worked really well. – Jodrell Apr 18 '11 at 08:25
  • The general guideline is that your application should not throw more than 1000 exceptions a second. When you throw exceptions for exceptional scenario's performance will in most cases not be a problem. – Steven Apr 18 '11 at 08:28
  • 1
    @Jodrell: Exactly! Error codes are useful in languages that don't have exception handling. In c# you hardly ever want to return error codes, because it is so easy to forget to check the error code of a called method. – Steven Apr 18 '11 at 08:29
  • @Jodrell I can't tell if what you said was a joke or serious? ;-) – bytedev Jun 07 '13 at 13:58
  • 1
    @nashwan, I definitely wasn't serious. In response to performance concerns, Exceptions appear much slower when debegging than they actually are. As long as Exceptions are exceptional everything is good. In fact, when the code works, exceptions take exactly no time, return codes still have to be checked. – Jodrell Jun 07 '13 at 14:13

5 Answers5

18

Throw an exception to provide more information (type of exception, message, etc.) for proper handling and to signify that:

  1. your code is being used inappropriately / illegally
    1. i.e. against contractual constraints that cannot be enforced during compile time
  2. an alternative to the primary flow has occurred
    1. i.e. you expected an operation to succeed but it failed, like obtaining a resource or connection

I would actually discourage returning "log objects" (i.e. returning an exception object rather than throwing one) as this

  1. results in unnecessary mashes of if statements to check the result AND handle a potential error
    1. all your methods would have to return of a "log object" (or have an out param) or else you cannot "bubble up" the error/exception for scoped handling, resulting in further limitations
  2. loses the helpfulness of try/catch/finally
  3. hurts readability of scope (operation attempt vs error handling vs clean up)

If you want to return "log objects", you should really use boolean returns with methods that makes sense (i.e. FindCity) or methods with an out boolean parameter (i.e. TryFindCity). This way, you don't need to specify flags, but just use the methods whose boolean return allows you to determine the would-be flag value.

EDIT

Per OP's comment, if there is a giant method with numerous possible validation errors, then the giant method should be refactored to call smaller methods that each throw the appropriate exception. The giant method can then simply re-throw each exception or just allow each exception to bubble up if it shouldn't be the one handling it.

If there are validation dependencies that prevent "proper separation", then simply throw a single ValidationException with the proper argument(s). An example of what this class could be is below:

public class ValidationException : Exception {
    private readonly object _Field;
    public object Field { get { return _Field; } }

    public ValidationException(string message) : base(message) { }

    public ValidationException(string message, object field)
        : this(message) {
        _Field = field;
    }
}

Then you can just throw one exception explaining the validation error.

bitxwise
  • 3,534
  • 2
  • 17
  • 22
  • more detailed/eloquent version of what I was trying to say. – Jodrell Apr 18 '11 at 08:28
  • 1
    Start using output params and you have just given up coding in OO. IMO output params lead to spaghetti code that is a nightmare to maintain and refactor. I personally never use them (you don't need to). If you need to get more than one "piece" of information out of a method then create a new class and encapsulate all the information there. – bytedev Jun 07 '13 at 14:03
  • Validation using exceptions? Sounds like something is wrong. [Fowler says "you usually shouldn't be using exceptions to signal validation failures"](http://martinfowler.com/articles/replaceThrowWithNotification.html). This is also discussed on [Stackoverflow](http://stackoverflow.com/questions/410558/why-are-exceptions-said-to-be-so-bad-for-input-validation). – Lightman Oct 28 '15 at 17:54
  • 1
    @Lightman - Firstly, thanks for your comment and article references. For common form validation, I would agree with you and the article you referenced. However, please note the OP's original question. The question was then extended to include some light validation that integrated with his original problem. I like that Martin Fowler had a section on "When to use this refactoring". I think context is very important. Also, throwing an exception when specified arguments are "invalid" is an acceptable approach to enforce code contracts. Again, I must emphasize that context is important. – bitxwise Nov 04 '15 at 19:49
6

If you're concerned about the performance of exceptions, you could factor towards the "tester-doer" pattern, which can be used to avoid them in most cases (it also makes the code more readable than a try/catch in my opinion).

// Traditional try catch
try
{
    var record = myDb.myTable.Select(primaryKey);

    // do something with record...
}
catch (RecordNotFoundException)
{
    // The record not found in the database... what to do?
}

// Tester-doer pattern
if (myDb.myTable.RecordExists(primaryKey))
{
    var record = myDb.myTable.Select(primaryKey);

    // do something with record...
}
else
{
    // The record not found in the database... what to do?
}

Same result, no exceptions. The cost is you have to write the "RecordExists" function yourself, usually it would be as simple as doing something like return COUNT FROM myTable WHERE PrimaryKey=foo == 1

MattDavey
  • 8,897
  • 3
  • 31
  • 54
  • +1 for tester-doer instead of error codes. Error codes just suck. – Steven Apr 18 '11 at 08:30
  • I mentioned the same thing, but didn't know it was called the "tester-doer" method - learn something new every day! – bitxwise Apr 18 '11 at 08:39
  • 1
    A TryDoSomething method is another example of the tester-doer pattern as well - a good example in the FCL being the TryParse methods :) – MattDavey Apr 18 '11 at 12:21
  • 6
    Be careful doing this with a concurrent system... There is no guarantee the record will exist between checking for it and selecting it, so you will still have to be ready to handle exceptions. – Jarrod May 22 '12 at 14:41
  • @Jarrod yes this is important, I would recommend doing this within a transaction and carefully considering an appropriate isolation level (avoiding phantom reads in this case) – MattDavey May 22 '12 at 17:31
  • @Jarrod +1 I was just about to say this. Same reason why checking File.Exists before you perform an action on the file is generally daft in enterprise systems (still see it used though) – bytedev Jun 07 '13 at 14:22
3

To turn things on thier head slightly. Consider, do I know how to handle this condition within the function and will the code be called frequently enough to make writing it worth while or, more generally, is this exceptional?

If the case occurs in the routine operation of your function then handle it as part of your return value. If it occurs because your function has been misused or some resource is unavailable throw an exception.

Jodrell
  • 34,946
  • 5
  • 87
  • 124
2

If the caller could have prevented an exception (like knowing that the city doesn't exist), throw an exception. Otherwise return an error.

I would never return a log object. It makes the code unreadable since you have to add if statements and special handling everywhere. It's better to add methods like CheckIfCityExists.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • But what is All those checks are part of a big Process let's say GiantMethod() and if this method is called from UI and if CheckIfCityExists fail? I don't want to write many catch blocks to inform the user for each validation failure. – pencilCake Apr 18 '11 at 08:17
  • 2
    Then you need to refactor since you have plenty of code smells. http://www.soberit.hut.fi/mmantyla/badcodesmellstaxonomy.htm. Methods should be small and perform a specific task. Not large and trying to do everything. – jgauffin Apr 18 '11 at 08:18
  • 1
    In my applications I even use exceptions to communicate user errors to the end user. I throw `ValidationException`s from the business layer. The UI catches those (and only those) and displays a user friendly message to the end user. – Steven Apr 18 '11 at 08:34
  • btw: +1 for you answer @jgauffin. – Steven Apr 18 '11 at 08:35
  • 1
    @Steven: I do the same thing. Although I tend to do validation in the user layer first. Models should be in a valid state when entering the business layer, hence exceptions are OK for validation there. – jgauffin Apr 18 '11 at 08:43
1

Generally speaking you would return an exception, only when something absolutely unexpected occurs. If its a condition you are aware of and can happen then you should try and handle it either by sending an error code or better still, format your code in smaller fragments,

Example

  1. Method call to check if the City is Found
  2. Proceed to other statements
Devraj
  • 3,025
  • 24
  • 25
  • I wouldn't say "unexpected". You'd throw an exception because you anticipated wrong usage without the support of contracts. For example, and IndexOutOfRange exception anticipated a negative index value. – bitxwise Apr 18 '11 at 08:00
  • you must always except the unexpected :-) – Jodrell Apr 18 '11 at 08:01
  • If you really don't want a calling method to ignore your error you need to throw an exception. The caller will have to explicitly write code then to handle the problem. (Personally I wouldn't call returning false from CityIsFound() an error code). – bytedev Jun 07 '13 at 14:27