I've been looking around too for a solution on how to catch exceptions thrown by contract conditions. It is always a good idea to explicitly throw the exceptions that can occur. Wether you want to catch them so your code doesn't halt with a nice big bang;depends on what data is validated. I also use contracts for user input validation. With contract preconditions you can enforce user input to conform to certain requirements (like not null or empty string).Second, you can use contracts to validate your own internal code (especially calculations) and enforce not only parameter input being valid but also the result of calculations being valid.
It is possible to catch exceptions thrown by contract conditions; simply put the calling code inside a try-catch block and explicitly catch the type of exception(s) that your condition(s) will throw. I would only do that with user input validation. Because when contract conditions set up to not only verify parameters but also essential code logic throw an error; something might be wrong with your code logic rather than the parameter values. In that case it is better to halt the program completely. But if you want your program to terminate in a more controlled way, you can catch them though. It is then up to you to verify if it is safe to let the program continue or not.
And I found out it is also possible to check for null references on events (at least created by yourself). I used it in my own sample code that also catches the contract thrown error. You need to pass the event or the object that calls the event as an additional parameter to access the event. 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 program main method. I commented out the lines where I add event listeners so the contract rules defined above will throw the nullreference exception. This is how to catch them without terminating with a big bang:
//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 perform null reference checking on events by using the new .NET 4.5 contract abbreviators:
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");
}