Tuesday, March 3, 2009

Defensive programming – Assertions


What are Asserts?
Assertions are key component for proactive programming. If they are used in a correct way they tell where and why error happened.
Assertion declares that some condition must be true, if it is not, the assert fails, bringing up message on failed condition (screen shot below).


Asserts are used only on Debug builds ( the DEBUG must be defined - it is enabled by default in debug builds in Visual Studio )
If you will take a look in a retail build assembly using the .Net Reflector tool the Debug.Assert(...) code will not be in the assembly.


How to Assert?
There are two basic rules:
  1. Assert must not change values or state of program (treat as read only)
  2. Rule: Check a single item at a time.

What to Assert?
Generally you should use Asserts for:
  1. Parameters coming into method. Sometimes better approach is to throw InvalidArgumentException
  2. Values returned by methods; on wrong returned program may die after 20 minutes (Asserts help! )
  3. Use asserts when you need to check for assumption (e.g. Available memory )

How to use Asserts in .Net Framework

The Debug class is defined in System.Diagnostics namespace. The Debug class methods allows to print debugging information and check application logic with assertions, making more robust without impacting the performance and code size of retail assemblies.

The Debug class except of Assert method, in which we are the most interested, provides following write methods: Write, WriteIf, WriteLine and WriteLineIf.

Lets take a look at the Debug.Assert method, There are three overloaded versions of the method.
  • Debug.Assert(i > 10);
  • Debug.Assert(i > 10, "i > 10");
  • Debug.Assert(i > 10, "i > 10", " i should be greater than 10, if you see this it means that it was not ;)");
The first parameter is a boolean condition, second is a string message and the third parameter is a string detailed message.
  • If assert is used in a similar way as below it does not tell us which parameter was bad
// wrong way to write assertion. Which param was bad?
public static void DoStuffWithObject1(int i, object o, int iLen)
{
    Debug.Assert((i > 0) && (null != o) && (iLen < MAXVAL ));
}
  • And here is the right way of doing this
// right way to write assertion - every param individually
public static void DoStuffWithObject2(int i, object o, int iLen)
{
    Debug.Assert(i > 0);
    Debug.Assert(null != o);
    Debug.Assert(iLen < MAXVAL ));
    ...
}
  • Asserts should check a value completely, in following example we expect that a parameter will be a valid customer name, what happens if an empty string is passed?
// incomplete checking 
public static bool GetCustomerName(string CustName)
{
    Debug.Assert(null != CustName, "null != CustName");

    return true;
}
  • And here example of complete checking
// complete checking
public static bool GetCustomerNameBetterChecking(string CustName)
{
    Debug.Assert(null != CustName, "null != CustName");
    Debug.Assert(0 != CustName.Length, "CustName is EmptyString");

    return true;
}
  • Be careful because improper using of Asserts can crash application!
    if the null is passed as a parameter the NullReferenceException is thrown
// Assert that crashes application if CustName is null
public static bool NullReferenceExceptionCrash(string CustName)
{
    Debug.Assert(CustName.Length > 0);

    return true;
}

No comments: