Code contracts, what to do with exceptions

My main question is what to do with the Exceptions that I throw.

For instance:

Contract.Requires(foo != null); 

What to do at higher levels of function call with this excluded exception?

Should I ignore him, and when I see that he knows that something is wrong with my design and fix it?

But what happens in the deployment if I deploy without Contracts and I get an argument that is foo == null, and my logic has no idea how to handle such an argument. Then everything will work.

Can someone explain how to deal with all of these scenarios?

thanks

+4
source share
5 answers

My main question is what to do with the Exceptions that I throw.

First of all, you technically cannot catch these exceptions. All methods except Contract.Requires<TExc>() throw a System.Diagnostics.Contracts.__ContractsRuntime.ContractException , which is built into your assembly and is private. To catch it, you need to catch all the exceptions, which is the worst thing you could do.

Contracts, as well as statements, are conditions in which it must always be true. If this is not the case, then the program is in a state in which it is not intended, and you cannot be sure that it can continue safely. You can think of contracts as language extensions. You do not expect the .NET program to allow you to violate type safety in the "special case", right? The same can be said of contracts.

What to do at higher levels of function call with this excluded exception?

The whole idea of ​​contracts provides a caller to check before calling the method with contracts. And if the caller does not check and does something wrong, it must be fixed. I mean: if you have a method with Contract.Requires(arg != null) , well then don't call it if you have a null value.

Another question: "Do you have to leave all contracts in the released bits or not?" From a security position, you better keep them all.

If your code does not expect any values, but it receives them, the only absolutely safe solution is to stop the current operation with an error. You cannot be sure that if you ignore your contract, you will not corrupt the data or do other bad things. Of course, you need a certain degree of detail so that your program continues in a safe state, and does not end with a big blow, although in some cases termination is required.

Should I ignore him, and when I see that he knows that something is wrong with my design and fix it?

If you release your software and find that there is a use case that does not work due to a contract failure, it probably will not work, even if there are no contracts, you simply did not think about it and to do additional work to support it. You must take care to carefully design all use cases and conduct a thorough QA to avoid this. Contracts are not relevant to these issues.

But what happens in the deployment if I deploy without Contracts and I get the argument foo == null, and my logic has no idea how to handle such an argument. Then everything will work.

This is another reason to leave contracts in place. Better to have a crash in a pre-designed place than anywhere else, you don't expect this to happen.

Perhaps the only significant reason for removing some contracts is performance: checking invariants after each method can be very expensive.

+18
source

Code contracts allow you to more accurately declare which arguments your method accepts, as well as what it returns (preconditions and postconditions). Instead of having a function that takes a string (any string), you can declare that the string must be non-zero, have a length greater than 10, consisting entirely of uppercase characters, etc.

If the caller does not adhere to the contract, it is a mistake and should be indicated as such (for example, an exception should be thrown). However, placing Contract.Requires() statements in your source does not generate any real IL code. You must run the Code Contracts script writer for the post-process code. This will introduce a contract check in the last IL, and these checks will throw exceptions if the contract is not respected.

You can also use the Code Contracts Static Controller to prove that contracts are applied throughout the code. If this is true, you can instruct the copyist not to insert checks because you have already proven that contracts are always respected. With the public API, you cannot do this because the static controller does not know how your code will be called. However, if you declare code contracts in your public API, your caller can use static checking to make sure his code is correct.

So, to answer your question, you should expect your interlocutor to stick to your contract. You must use a rewriter to insert checks and fail in a controlled manner if the caller does not actually adhere to your contract.

MSDN has an article on Code Contracts , which is a good starting point for exploring concepts.

+3
source

In addition to Pavel Gatilov, answer, remember that you can always throw specific exceptions for preconditions

 Contract.Requires<ArgumentException>(foo != null, "foo"); 

These can be captured and processed by the user and can provide them with a means of processing invalid input, for example: by displaying a warning to the user if he enters some invalid data.

+2
source

To take your specific example, the contract tells you that the argument to the containing method should not be null (and ideally, the documentation for this method will also tell you about this). When writing a consumption code, you must acknowledge this fact, and if you do not have a nonzero value that can be passed as an argument to a method, you should not call the method. It is then that the consumer code must decide what to do in such circumstances. This can be either an alternative execution path or an exception. As @sll says, the choice of action in this case depends entirely on the logic required by the needs of your application.

0
source

I was also looking for a solution on how to catch exceptions caused by contract terms. It is always a good idea to explicitly throw exceptions that may occur. Wether you want to catch them so that your code does not stop with a big hit, it depends on what data is checked. I also use contracts to validate user input. With the preconditions of the contract, you can force input of user input in accordance with certain requirements (for example, not an empty or empty string). Secondly, you can use contracts to verify your own internal code (especially calculations) and ensure not only the input of parameters, but also the result of calculations.

You can catch exceptions to the terms of the contract ; just put the calling code inside the try-catch block and explicitly catch the type of exceptions (s) that will cause your conditions (states). I would only do this with user input validation. Since , when the conditions of the contract are configured not only to check the parameters, but also for the logic of the main code, output an error ; something may be wrong with your code logic, not with parameters. In this case, it is better to completely stop the program . But if you want your program to end in a more controlled way, you can catch them. Then you need to check whether the program can continue or not .

And I found out that you can also check for null references to events (at least created by yourself). I used it in my own code example, which also catches the error caused by the contract. You need to pass the event or object that triggers the event as an additional parameter to access the event. The following code is part of what I have in one of my classes:

 public delegate void Transactie(Rekening rekening);//signature for events public Transactie RekeningUittreksel; public Transactie NegatiefSaldo; public void Storten(decimal bedrag,Transactie actie) { Contract.Requires<NullReferenceException>(actie!=null,"\n\nNo event listeners have been added yet!\n\n"); VorigSaldo = Saldo; Saldo += bedrag; RekeningUittreksel(this); } public void Afhalen(decimal bedrag,Transactie actie,Transactie actie2) { Contract.Requires<NullReferenceException>(actie!=null,"\n\nNo event listeners have been added yet!\n\n"); Contract.Requires<NullReferenceException>(actie2 != null, "\n\nNo event listeners have been added yet!\n\n"); VorigSaldo = Saldo; if (bedrag <= Saldo) { Saldo -= bedrag; RekeningUittreksel(this); } else { NegatiefSaldo(this); } } 

Next is part of the main program method. I commented out the lines where I add event listeners, so the contract rules described above will throw an exception using noreference. Here's how to catch them without stopping with a big hit:

  //mijnZichtrekening.RekeningUittreksel += pietjePek.ToonUittreksel; //mijnZichtrekening.NegatiefSaldo += pietjePek.ToonNegatief; try { mijnZichtrekening.Storten(50m, mijnZichtrekening.RekeningUittreksel); } catch (NullReferenceException ex) { Console.WriteLine(ex); } try { mijnZichtrekening.Afhalen(100m, mijnZichtrekening.RekeningUittreksel, mijnZichtrekening.NegatiefSaldo); } catch(NullReferenceException ex) { Console.WriteLine(ex); } 

I rewrote some code a bit to check for null event references using the new .NET 4.5 contract abbreviations:

  public void Afhalen(decimal bedrag) { NegatiefSaldoHasListeners(this.RekeningUittreksel, this.NegatiefSaldo);//calls the contract abbreviator with delegate type parameters to check for Nullreference VorigSaldo = Saldo; if (bedrag <= Saldo) { Saldo -= bedrag; RekeningUittreksel(this); } else { NegatiefSaldo(this); } } public void Storten(decimal bedrag) { UittrekselHasListeners(this.RekeningUittreksel);//calls the contract abbreviator with a delegate type (event) parameter to check for Nullreference VorigSaldo = Saldo; Saldo += bedrag; RekeningUittreksel(this); } public virtual void Afbeelden() { Console.WriteLine("Rekeningnr: {0:0000 0000 0000 0000}",Nummer); Console.WriteLine("Saldo: {0}",Saldo); Console.WriteLine("Creatiedatum: {0:dd-MM-yyyy}",CreatieDatum); } [ContractAbbreviator] public void CheckArgs(string nummer, Klant eigenaar) { Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(nummer), "Geen nummer ingevuld!"); Contract.Requires<FormatException>(nummer.Trim().Length == 16,"Ongeldig aantal tekens ingevoerd!"); Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(eigenaar.ToString()), "Eigenaar niet opgegeven!"); } [ContractAbbreviator] public void UittrekselHasListeners(Transactie actie) { Contract.Requires<NullReferenceException>(actie != null, "\n\nGeen event listener toegewezen!\n\n"); } [ContractAbbreviator] public void NegatiefSaldoHasListeners(Transactie actie,Transactie actie2) { Contract.Requires<NullReferenceException>(actie != null, "\n\nGeen event listener toegewezen!\n\n"); Contract.Requires<NullReferenceException>(actie2 != null, "\n\nGeen event listener toegewezen!\n\n"); } 
0
source

Source: https://habr.com/ru/post/1385642/


All Articles