I apologize in advance for a somewhat vague question, which may have, perhaps, several answers, but nevertheless, the practical aspect is of primary interest.

So, there is a certain not very large project consisting of a number of independent modules, each completely independent of itself and written by different people at different times.

Often in the code I see different (which is natural, imho) error catching options, from quite suitable and relevant to just try{...} catch{MessageBox.Show("Ошибка!");} (Yes, and this does not happen), often critical places without any try blocks.

The task is to bring this business (error handling) to a standard, at least within the framework of this project, and then the fun begins - in many places there are repetitive operations, for example, when working with files and directories, and in such places you often have to either write, or already have constructions of the form:

  if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException(path, "Путь к каталогу должен быть задан."); } 

Or there are similar constructions where, in the event of an error situation, an exception of some type is raised (for example, IOException ) with some textual description. At the same time, there is no (almost no, honestly) inside the project any sort of class hierarchy for error handling, standard errors are used mainly.

So, initially the view on the general infrastructure of error handling and the approach as a whole was interesting. But this aspect has already clarified for itself.

But for the time being I don’t really understand what would be the best approach with the generation of error text messages both for the modules of the project itself and for the logger? (used logger, able to work with Exception instances).

So far the first thing that came to mind is to put all the messages in a separate file in the form of constants and use them by connecting the file to each of the modules. Or maybe drive all the resources?

But in general, community views are interesting, what other practical implementations and approaches, techniques would be appropriate and convenient in everyday use, taking into account the described context? Perhaps there are some more effective, convenient and proven methods or means?

PS

  1. Localization of the project into several languages ​​- not yet.
  2. It would not be very desirable to make significant changes to the code of the modules (one would have to manage with a little blood).
  3. I would like to avoid third-party solutions if, of course, they do not solve the problem systematically and are applicable in the future (I remembered EurekaLog in Delphi).

Thank you for your attention to the question!

  • one
    It is necessary to divide the messages into 2 types: messages for the user of the program, which are better stored in resources, at least from the point of view of possible localization, and messages for the programmer, which are usually difficult to carry out somewhere separately because it is inconvenient, and better to keep them " in place, at hand. " Plus you do not forget about unit tests: after all, there will be several tests for each method, among which there will be specially causing exceptions (for example, incorrect input parameters). - Bulson
  • @Bulson, thanks! Commentary on the case, and took into account what I completely forgot - about testing. - BlackWitcher

1 answer 1

Errors need to catch try / catch / finaly only in those places where they are expected and only those types that are expected, a banal example of calling the method, in the method checking for, for example, the availability of this call to the current user, the expected exception, say PermitionDeniedException, in the block catch it and you need to catch it, the rest (for example, ArgumentNullException) should be forwarded to the top of the stack and process "globally", possibly with a total crash of the application.

 [System.Serializable] public class PermitionDeniedException : System.Exception { public PermitionDeniedException() : base("Пользователю не доступен этот метод") { } public PermitionDeniedException(string message) : base(message) { } public PermitionDeniedException(string message, Exception inner ) : base( message, inner ) { } protected PermitionDeniedException(SerializationInfo info, StreamingContext context) : base(info,context ) { } } try { SomeMethod(); } catch (PermitionDeniedException ex) { _logger.Error(ex, "Сообщение") } private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) { var ex = e.ExceptionObject; if (ex.GetType() == typeof(ExceptionType1)) { _logger.Error(ex, "Сообщение"); // TODO: Уведомить пользователя } else if (ex.GetType() == typeof(ExceptionType2)) { _logger.Fatal(ex, "Сообщение"); // Экстренно завершить приложение } else { _logger.Fatal(ex, "Сообщение"); // Можно сделать дамп и завершить приложение } } 

For Winforms, somewhere in the Main method you probably should subscribe to AppDomain.UnhandledException. For example: System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper; In the UnhandledExceptionTrapper handler, you can get an exception type, check its severity and, depending on it (criticality), either drop urgently, telling the user something like “The program has fulfilled an unacceptable error and will be closed” or continue working, but again informing the user. As for multilingualism, then we need dynamic resources and resource dictionaries, you can store lines in them that can be used to describe the exception.

P.S. The structure of exclusion is worth considering, it can be single-level, which is more likely even a plus. The most common (roots) must be inherited from System.Exception , which writing in a message is also not difficult to invent if you have an elaborate exception structure.

For error logging it is best to use ready loggers, or Trace. The latter is not so flexible, but you do not need to drag anything from nuget. From ready last time I use NLog.

A little messy in my opinion, so I will be glad to criticism.

  • Personally for my part, there will be no criticism - everything is clear in words and in code, too. We did not forget about the logger, but this is another moment (as long as a simple class is used for logging, then we will see, in addition to NLog, there is another Log4net from the known ones, but we haven’t been able to work with it yet). Thank you very much for your answer! I don’t accept it just because I have a vested interest :-) simply - I would like to hear more opinions, but Friday, evening ... - BlackWitcher
  • one
    @BlackWitcher log4net is not used only because it seems to have been abandoned, but now with nlog I want to stick a couple of targets into the code and make it dependent on some simple interface with configs and pull the package into the nuget, but for some reason the comrades don't listen, they don't want to use my code in new projects, every time the logging system is sawing a new one, as well as exception sets :) And I am always happy to help :) - kimaman2
  • The idea is interesting, but, as in my opinion - the main thing that is useful. If there is a rather elaborate code that solves a particular problem (in this case, logging), why not use it? Yes, especially with configs, customizable, and with the preparation of sets of exceptions, which could be supplemented with their own, in case of need. For example, I would love to use it in production, so if you issue it as a nuget package, I hope you share it with the community? - BlackWitcher
  • @BlackWitcher Yes, I’m talking about the kitchen at work ... And so far I don’t even have an account on my github) - kimaman2
  • I understood that it was a question of work, but if it was not a pity, then why not bring something useful to the masses? And accounts - it can be acquired :) - BlackWitcher