For example, what to use when you need to create a column in a table only if there is no such column in it?

I can, how to write code that will check if a column exists, and only then add without errors, so I can try to add a column without checking by wrapping a method in try-catch ( если есть is, an exception is caught ; если нет , the column will be added ) .

The result of the work will be the same.

And there are a lot of such examples, for example, you can check the files for existence, and only then do the copying, and you can catch exceptions.

Which method is more literate, or correct?

  • If possible, exceptions should be avoided. They are designed for exceptional situations. - Dmitry D.
  • @DmitryD. only this possibility is far from always ... - Pavel Mayorov
  • @DmitryD., "Avoid exceptions" - this can be understood in different ways. I used to know such programmers who, following this principle, have never in their career written lines of code that generate exceptions. - Astronavigator
  • 3
    Classic related article: Vexing exceptions . - VladD
  • Please add tags for the language, or any-language label - Abyx

2 answers 2

Exceptions allow you to make the code cleaner and clearer, since they can be used to separate the execution of actions and error handling. In Martin’s Pure Code book, this aspect is described first.

At the same time, in my experience, it is necessary to separate the code: carry out the try / catch in a separate method. The programmer who will deal with your program will say thank you. This construction is quite cumbersome, even in the simple form of try / finally , and in the middle of a large method will puzzle anyone.

The second plus of exceptions is that they allow the transfer of additional information. The atoi function from C could not say anything about why it was not possible to convert the string to an integer.

 int result; result = atoi("123"); /* в result 123 */ . . . result = atoi("foo"); /* что в result? */ 

In languages ​​such as Java and C #, you can add necessary properties to your exception class, combining any error codes with call contexts and something else . Example:

 . . . catch (SqlException e) { Console.WriteLine("Ошибка '{2}' в строке {0} процедуры {1}", e.LineNumber, e.Procedure, e.Message); } . . . 

Developers who have long and firmly passed to the exceptions, make their code even cleaner, without returning any error codes, in particular, the notorious null :

 // Что будет, если в хранилище нет пользователя с указанным userid? // Вернёт null или сгенерирует исключение? User user = userRepository.GetById(userId); 

At present, it is considered that it is more correct to create an exception (referring again to the book “Clean Code” for arguments). If the method is used to check the presence of a user, it is recommended to convert it into a TryX form. It is clumsy, but already familiar, at least for .NET programmers:

 User user; if (userRepository.TryGetById(userId, out user)) { . . . } 

What is more valuable, it is unequivocal: looking at the code, you do not think: “what if there is no such user?”

Now let's look at the situation from the other side - and when should we not use exceptions? In my opinion, when they make it difficult to understand the code. If the situation is not exceptional , then the program text should be a regular check.

For example, the application for the new document generates the names Untitled.foo , Untitled1.foo , Untitled2.foo , etc.

The situation that a file with the same name already exists is quite ordinary, not exceptional, therefore, it is more correct to implement the code using the usual check:

 public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do { filename = prefix + (++suffix).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } 

This code is not only faster than similar code with the use of exceptions, but more importantly, it is more understandable to other programmers, because it implicitly conveys additional information to them: this thing will happen regularly, and we are ready for it.

And here, for example, is an incredible situation that 2 billion untitled files have accumulated in the folder - an undoubted exception.

 public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do { if (suffix == int.MaxValue) throw new InvalidOperationException("You're crazy!"); filename = prefix + (++suffix).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } 

This code looks more confusing. Fortunately, we can put some of the checks on the C # compiler:

 public string GetNewDocumentName(string prefix) { var filename = prefix + ".foo"; if (!File.Exists(filename)) return filename; int suffix = 0; do checked { filename = prefix + (suffix++).ToString() + ".foo"; } while (File.Exists(filename)); return filename; } 

Whence another rule arises: the code can be made cleaner if one knows the language, platform, library, and relies on their exceptions.

I wrote above that exceptions are performed more slowly than checks, and I want to clarify my point: do not rely on performance when making decisions. The correct transfer of meaning to another programmer, the purity of the code is what you should strive for. The difference in performance, although it exists, has never been so great that users notice it. Well, if only you do not write the code for the most nested loop in any graphics engine.

    Preliminary checks work faster and are often more accurate. But only exception handling gives a full guarantee.

    Example: simple file creation. Bugs here are possible - the sea:

    • The name may contain prohibited characters (the set of allowed characters depends on the file system!);
    • the full path to the file may be too long;
    • the file may have already been created;
    • may not have access to create files;
    • A disc may be write-protected or not support it at all (CD-ROM is an example of this).

    All these options can be checked in advance - and give the user a clear message in Russian.

    But there are situations that not a single preliminary check will catch:

    • the file can be created by another program immediately after checking;
    • the file can be blocked by the antivirus;
    • if the file is created on a network share, the network can "fall off".
    • one
      Does it make any sense to do 5 checks (take your example), when can I do 1 action and catch an exception? Less code is obtained. - iluxa1810
    • @ iluxa1810 Yes, it does - the test can go faster than trying to create a file. Plus, a failed check in time can cancel a complex action (say, preparing data for saving to a file). But, as a rule, it is easier to catch an exception. - Pavel Mayorov
    • 6
      @ iluxa1810 in my opinion, you need a reasonable combination of checks, to interrupt the execution of the algorithm when it’s so clear that it’s not worth doing anything further and catching exceptions, for all other cases - rdorn
    • @ iluxa1810 Do you think that you will not have five different catch for different exceptions (throw everything in one!), and if they do, they will be treated the same? - AK