Hi, Habr! There are several ways to validate arguments. For example, to check for null you can use:
In the article I will consider only the third option (all code samples are for C #, however some of them will be useful in Java).
Most often in a project, to eliminate copying of the same code, someone creates a static class in which you can check the field for null, more than zero, etc.
However, in this case it is forgotten that one and the same verification can be extremely useful for validating arguments (in this case, the name of the argument is passed as an additional parameter), and for checking inside the method (in this case, another exception is thrown).
So, for a start it is best to agree in advance on the naming of both cases. For example, Guard.IsNotNull
for the body of the method and Guard.ArgumentIsNotNull
for the arguments
Immediately examples of erroneous code:
Guard.IsNotNull(connection, $"Unable to setup connection with host {host} and port {port}") Guard.IsNotNull(connection, string.Format("Unable to setup connection with host {0} and port {1}", host, port)) //
Both examples above generate a string for each check (in theory, all Guard do not throw exceptions on the combat server, that is, we generate quite a few lines just to be eaten by the garbage collector).
Corrected version:
public static class Guard { public static void IsNotNull(object value, string format, params object[] formattingArgs) // , }
In fact, the option above is also bad. It does not create a string anymore, but for each method call a new array formattingArgs is created. Of course, less memory is allocated for it, but such methods will still load on the garbage collector.
The most annoying thing is that the program will slow down, but simple profiling will not cause a problem. Your program will simply stop to clear memory more often (I fixed this error in one of the programs, garbage collection began to occupy instead of the previous 15% only 5%).
So, we proceed in the same way as done in string.Format : we generate more methods for different numbers of arguments.
public static class Guard { public static void IsNotNull(object value, string errorMessage) public static void IsNotNull(object value, string errorMessageFormat, object arg1) public static void IsNotNull(object value, string errorMessageFormat, object arg1, object arg2) public static void IsNotNull(object value, string errorMessageFormat, object arg1, object arg2, object arg3) }
So, now the array will not be allocated. However, we automatically received a new problem - Boxing.
Consider the method call: Guard.IsNotNull(connection, "Unable to setup connection with host {0} and port {1}", host, port)
. The variable port is of type int (most often at least).
It turns out that in order to pass a variable by value, .Net will create an int each time on the heap to pass it as an object. This situation will occur much less frequently, but it will still be.
And another problem - if the original object being checked is the value type (for example, we check for null in a generic method that has no type restrictions).
You can fix this by creating Generic methods for checks:
public static class Guard { public static void IsNotNull<TObject>(TObject value, string errorMessage) public static void IsNotNull<TObject, TArg1>(TObject value, string errorMessageFormat, TArg1 arg1) public static void IsNotNull<TObject, TArg1, TArg2>(TObject value, string errorMessageFormat, TArg1 arg1, TArg2 arg2) public static void IsNotNull<TObject, TArg1, TArg2, TArg3>(TObject value, string errorMessageFormat, TArg1 arg1, TArg2 arg2, TArg3 arg3) }
As can be seen above, in order to conveniently check the values for null, we need:
Without code generation, it is relatively difficult to add new functions, much less change them.
The points below do not speed up your program, but just slightly improve readability.
Often ReSharper swears that the value may be null, although it seems to be checked with the help of Guard. In this case, you can either start gradually to block warnings in the code (which may be fraught), or explain to the verifier that everything is fine. The full list of attributes can be viewed here , but here are useful for us:
In theory, the number of exceptions to our functions on combat servers should be the smallest. Consequently, any actuation of them should carry a maximum of information: what actually happened (after all, the situation is rare). At a minimum, it is best to include in the text:
I hope this article will help you to relatively easily improve the checks in your code.
A little earlier, I implemented all the developments in the library (source code under the MIT license) - so you can just use it (or copy the code to yourself, etc.)
Source: https://habr.com/ru/post/330150/
All Articles