Click here to Skip to main content
15,880,608 members
Articles / Programming Languages / C#

Defensive Programming in C# (with T4)

Rate me:
Please Sign up or sign in to vote.
4.50/5 (5 votes)
2 Jun 2014CPOL7 min read 33.6K   16   14
The purpose of this article is to provide an simple library which try to meet some defensive issues focusing attention on how to manage return function value in a safe and unambiguous way.

Introduction

The purpose of this article is to provide an simple library which try to meet some defensive issues focusing attention on how to manage return function value in a safe and unambiguous way.

Background

Basic notions of C# and T4. Bad experience with unfindable bugs ,  and the will to write resilient code.

A glimpse at the matter

There are a lot of documentations about defensive programming. Some of these states short sentence like “i’m protecting my own code from myself” or “Never  Assume Anything” , they are good but but in some industry sector defensive programming have a more strict definition. For example Railway industry use CENELEC 50128 to drive software development to higher possible standard due to the sensitiveness of the matter they usually manage : human life.

CENELEC 50128 defines the processes and the techniques to enforce to meet the safety level defined in the Software Requirement Specification. For the higher safety level ( SIL 4 , Safety Integrity Level 4) defensive programming must be applied and in B.15 section there are a list of some techniques :

  1. Variable should be range checked
  2. Where possible , value should be checked for plausibility
  3. Parameters to procedure should be type , dimension and range checked at procedure entry
  4. Input variables and intermediate variables with physical significance should be checked for plausibility
  5. The effect of output variables should be checked , preferably by direct observation of associated system state changes

As everyone can imagine all these controls of code flow is not easy , is not cheap and is not fast ( i mean both computation time and developer effort). In my opinion defensive programming is based on paranoid concept of passing parameters : a method/class can’t trust in caller , in called and may be should not trust in itself. Often defensive and paranoid are words links together , too paranoid means too code or unuseful code , too relax means bugs , it’s all a matter of tuning the control over code. There are also some way to coding that i ( and not only i ) found dangerous:

  1. Passing parameter by reference 
  2. Using special value to point out a function had an error ( ex : null , -1)
  3. Using writable field in return function object.

These 3 point are the heart of this article. I would to make another punctualization about meaning of some words. I found in online dictionary that the difference between safe and secure is:

  • safe is something without risk.Latin : salvus -> sound , intact
  • secure is something\somebody under the cure of something\somebody.  Latin : securus -> quiet , without fear

On the other hand safety has in CENLEC 50128 at paragraph 3.21 the following definition : freedom from unacceptable level of risk. For these reason for me it’s better use the term safe when talking about return value of function.

After this brief introduction it’s time to show the code:

1.String

C#
/// <summary>
///
/// </summary>
public abstract class ReturnValueAC
{
    private Boolean priSuccess = false;

    public void SetFalse()
    {
        priSuccess = false;
    }

    public void SetSuccess(Boolean success)
    {
       priSuccess = success;
    }

    public Boolean Success
    {
        get {return priSuccess;}
    }

}

/// <summary>
///
/// </summary>
public class StringReturnValue : ReturnValueAC
{
    public StringReturnValue()
    {

    }

    public StringReturnValue(String outcome , Boolean success)
    {
        if (success == true)
        {
            this.Outcome = outcome;
            SetSuccess(success);
        }
    }

    public readonly String Outcome;
}

There is not a lot say about these two classes. An abstract class which contain a readonly property ( remember defensive suggestions?) that expose if a function has exit with consistent outcome. A concrete class specialized in returning the result of a function, in this case has a String type. The reason why i use an abstract and a concrete will be clearer in T4 section where will be presented the power to generate code in very easy way.

These ReturnValue classes are usable in this way:

C#
public class DefensiveClass
{
        public DefensiveClass()
        {
            RunSafe();
            RunUnSafe();
        }

        /// <summary>
        /// Remove from a string a substring 
        /// E1) If substring is no found then error
        /// E2) If one of parameters are empty or null then error
        /// </summary>
        /// <param name="parThis"></param>
        /// <param name="parThat"></param>
        /// <returns></returns>
        private StringReturnValue GetThisWithoutThat(String parThis, String parThat)
        {
            StringReturnValue ReturnValue = new StringReturnValue();

            if((String.IsNullOrEmpty(parThis) == false)  
                              && 
               (String.IsNullOrEmpty(parThat) == false))
            {
                String sReplace = parThis.Replace(parThat, String.Empty);
                if (sReplace != parThis)
                {
                    ReturnValue = new StringReturnValue(sReplace,true);
                }
                else
                {
                    // E1
                    ReturnValue.SetFalse();
                }
            }
            else
            {
                // E2
                ReturnValue.SetFalse();
            }
            return ReturnValue;
        }

        private void RunSafe()
        {
            StringReturnValue srv1 = GetThisWithoutThat("ClassMethod", "Method");
            StringReturnValue srv2 = GetThisWithoutThat("Class", "Method");
            String sFormat = "{0} Success = {1} , Outcome = {2}";
            String sSomethingGoesWrong = "[Do not care about it , it has no meaning]";
            String sMessage = String.Empty;

            if (srv1.Success == true)
            {
                sMessage = String.Format(sFormat, "srv1", srv1.Success, srv1.Outcome);
            }
            else
            {
                sMessage = String.Format(sFormat, "srv1", srv1.Success, sSomethingGoesWrong);
            }
            Console.WriteLine(sMessage);

            if (srv2.Success == true)
            {
                sMessage = String.Format(sFormat, "srv2", srv2.Success, srv1.Outcome);
            }
            else
            {
                sMessage = String.Format(sFormat, "srv2", srv2.Success, sSomethingGoesWrong);
            }
            Console.WriteLine(sMessage);
        }
    }

The first time the method is called with no problem :

C#
StringReturnValue srv1 = GetThisWithoutThat("ClassMethod", "Method");

srv1 Success = True , Outcome = Class

But the second time something goes wrong :

C#
StringReturnValue srv2 = GetThisWithoutThat("Class", "Method");

And print this message :

srv2 Success = False , Outcome = [Do not care about it , it has no meaning]

If i were used a function that simply make replace and return a String ,  and check for the same constraints describe in comment  , what will return? 

  1. A null value is possible but is to avoid in defensive because i do not trust in caller and  simply could raise an exception if not properly managed. Remember Finagle's law : “Anything that can go wrong, will—at the worst possible moment.”  and I prefer not let a null run free into code.
  2. It can return “” , but what really mean? We have an ambiguity because also with two equal string return “” , and it’s not an error.
  3. Simply return “Class” , another ambiguity.
  4. Special value like = “ERROR”. If parThis = “ERRORMethod” we have a problem.

The following method shows how much more work we have to do, with less value , to  manage the same situation.

C#
enum unSafeCases
{
    CASE1,
    CASE2,
    CASE3,
    CASE4
}

/// <summary>
/// Remove from a string a substring
/// E1) If substring is no found then error
/// E2) If one of parameters are empty or null then error
/// </summary>
/// <param name="parThis"></param>
/// <param name="parThat"></param>
/// <returns></returns>
private String  GetThisWithoutThat(String parThis, String parThat,unSafeCases unSafe)
{
    String ReturnValue = String.Empty;
    String sReplace    = String.Empty;

    if( ( String.IsNullOrEmpty(parThis) == false) &&
        ( String.IsNullOrEmpty(parThat) == false) )
    {
        sReplace = parThis.Replace(parThat, String.Empty);
        if (sReplace != parThis)
        {
            // If it's all ok return the same value but ...
            ReturnValue = sReplace;
        }
        else
        {
            // E1
            switch(unSafe)
            {
                case unSafeCases.CASE1:
                    // Case 1
                    ReturnValue = null;
                    break;
                case unSafeCases.CASE2:
                    // Case 2
                    ReturnValue = String.Empty;
                    break;
                case unSafeCases.CASE3:
                    // Case 3
                     ReturnValue = sReplace;
                    break;
                case unSafeCases.CASE4:
                    // Case 4
                    ReturnValue = "ERROR"; // Or better "ERROR(1)"
                    break;
            }
        }
    }
    else
    {
            // E2
            switch(unSafe)
            {
                case unSafeCases.CASE1:
                    // Case 1
                    ReturnValue = null;
                    break;
                case unSafeCases.CASE2:
                    // Case 2
                    ReturnValue = String.Empty;
                    break;
                case unSafeCases.CASE3:
                    // Case 3
                     ReturnValue = sReplace;
                    break;
                case unSafeCases.CASE4:
                    // Case 4
                    ReturnValue = "ERROR"; // Or better "ERROR(2)"
                    break;
            }
    }
    return ReturnValue;
}

private void RunUnSafe()
{
    String sCase1_wrong = GetThisWithoutThat("Class", "Method", unSafeCases.CASE1);

    if (sCase1_wrong == null)
    {
        Console.WriteLine("Case1 is null");
    }
    else
    {
        //
    }

    String sCase2_wrong = GetThisWithoutThat("Class", "Method", unSafeCases.CASE2);

    String sCase2_right = GetThisWithoutThat("Class", "Class", unSafeCases.CASE2);

    if (sCase2_wrong == sCase2_right)
    {
        Console.WriteLine("Case2 : Who's right ? Who's wrong ?");
    }
    else
    {
        //
    }

    String sCase3_wrong = GetThisWithoutThat("Class", "Method", unSafeCases.CASE3);
    String sCase3_right = GetThisWithoutThat("ClassMethod", "Method", unSafeCases.CASE3);

    if (sCase3_wrong == sCase3_right)
    {
        Console.WriteLine("Case3 : Who's right ? Who's wrong ?");
    }
    else
    {
        //
    }

    String sCase4_wrong = GetThisWithoutThat("Class", "Method", unSafeCases.CASE4);
    String sCase4_right = GetThisWithoutThat("ERRORMethod", "Method", unSafeCases.CASE4);

    if (sCase4_wrong == sCase4_right)
    {
        Console.WriteLine("Case4 : Who's right ? Who's wrong ?");
    }
    else
    {
        //
    }
}

The complete output is :

C#
srv1 Success = True , Outcome = Class
srv2 Success = False , Outcome = [Do not care about it , it has no meaning]
Case1 is null
Case2 : Who's right ? Who's wrong ?
Case3 : Who's right ? Who's wrong ?
Case4 : Who's right ? Who's wrong ?

With this simple wrapper around primitive String class it’s possible manage return value with the confidence that there are no ambiguity in return code , one value is about the outcome of the operations occurred in that method and the other point out about if the operations are finished according to their boundary\constraints. Field Success must be think as a mark that certificate if Outcome has or not a worthwhile value , in this way caller method is warned not to use Outcome , meanwhile caller have not to know special value to manage error in called method.

2. Integer

What happened when we use some special value to point out that something goes wrong inside a function which return an integer? Following code show how to manage in a safe way the return value of a simple mean() function.

C#
public class DefensiveClassInteger
    {

        int[] arr1_wrong = null;
        int[] arr2_wrong = new int[] { };
        int[] arr3_wrong = new int[] { int.MaxValue, int.MaxValue };
        int[] arr1_right = new int[] { 3, -3 };
        int[] arr2_right = new int[] { 0, -2 };
        int[] arr3_right = new int[] { 0, -4 };

        public DefensiveClassInteger()
        {
            Console.WriteLine("Begin of Safe code");
            RunSafe();
            Console.WriteLine("End of Safe code");

            Console.WriteLine("Begin of UnSafe code");
            RunUnSafe();
            Console.WriteLine("End of UnSafe code");
        }

        private void Print(String message, Boolean AreUnequal )
        {
            if (AreUnequal == true)
            {
                Console.WriteLine(String.Format("{0} , Right is different from Wrong ! Do you expect something else?",message));
            }
            else
            {
                Console.WriteLine(String.Format("{0} , Confusion in progress!!",message));
            }
        }

        private void RunSafe()
        {
            intReturnValue irv1_wrong = MeanSafeEdition(arr1_wrong);
            intReturnValue irv1_right = MeanSafeEdition(arr1_right);

            Print("irv1",irv1_wrong.Success != irv1_right.Success);

            intReturnValue irv2_wrong = MeanSafeEdition(arr2_wrong);
            intReturnValue irv2_right = MeanSafeEdition(arr2_right);

            Print("irv2", irv2_wrong.Success != irv2_right.Success);

            intReturnValue irv3_wrong = MeanSafeEdition(arr3_wrong);
            intReturnValue irv3_right = MeanSafeEdition(arr3_right);

            Print("irv3", irv3_wrong.Success != irv3_right.Success);
        }

        private void RunUnSafe()
        {
            int irv1_wrong = MeanUnSafeEdition(arr1_wrong);
            int irv1_right = MeanUnSafeEdition(arr1_right);

            Print("irv1", irv1_wrong != irv1_right);

            int irv2_wrong = MeanUnSafeEdition(arr2_wrong);
            int irv2_right = MeanUnSafeEdition(arr2_right);

            Print("irv2", irv2_wrong != irv2_right);

            int irv3_wrong = MeanUnSafeEdition(arr3_wrong);
            int irv3_right = MeanUnSafeEdition(arr3_right);

            Print("irv3", irv3_wrong != irv3_right);
        }

        /// <summary>
        /// Return the mean of a array of int
        /// </summary>
        /// <param name="arrayOfInt"></param>
        /// <returns>ReturnValue.Success = true => ReturnValue.Outcome has a worthwhile value</returns>
        public intReturnValue MeanSafeEdition(int[] arrayOfInt)
        {
            intReturnValue ReturnValue = new intReturnValue();

            try
            {
                int iCounter = 0;
                if (arrayOfInt != null)
                {
                    if (arrayOfInt.Length > 0 )
                    {
                        for (int iIndex = 0; iIndex < arrayOfInt.Length; iIndex++)
                        {
                            // Why using "cheched" ?
                            // See :
                            // http://msdn.microsoft.com/en-us/library/system.overflowexception.aspx
                            // http://msdn.microsoft.com/en-us/library/6a71f45d.aspx
                            // http://msdn.microsoft.com/en-us/library/khy08726.aspx
                            iCounter = checked(iCounter + arrayOfInt[iIndex]);
                        }

                        ReturnValue = new intReturnValue(iCounter / arrayOfInt.Length, true);
                    }
                    else
                    {
                        ReturnValue.SetFalse();
                    }
                   
                }
                else
                {
                    ReturnValue.SetFalse();
                }
            }
            catch (Exception ex)
            {
                // OverflowException for example
                ReturnValue.SetFalse();
            }

            return ReturnValue;
        }

        /// <summary>
        /// Return the mean of a array of int
        /// </summary>
        /// <param name="arrayOfInt"></param>
        /// <returns>ReturnValue = ??? </returns>
        public int MeanUnSafeEdition(int[] arrayOfInt)
        {
            int ReturnValue = 0;
            try
            {
                int iCounter = 0;
                if (arrayOfInt != null)
                {
                    if (arrayOfInt.Length > 0)
                    {
                        for (int iIndex = 0; iIndex < arrayOfInt.Length; iIndex++)
                        {
                            // Why using "cheched" ?
                            // See :
                            // http://msdn.microsoft.com/en-us/library/system.overflowexception.aspx
                            // http://msdn.microsoft.com/en-us/library/6a71f45d.aspx
                            // http://msdn.microsoft.com/en-us/library/khy08726.aspx
                            iCounter = checked(iCounter + arrayOfInt[iIndex]);
                        }

                        ReturnValue = iCounter / arrayOfInt.Length;
                    }
                    else
                    {
                        // It's source of confusion
                        ReturnValue = -1;
                    }                
                }
                else
                {
                    // It's source of confusion
                    ReturnValue = 0;
                }
            }
            catch (Exception ex)
            {
                // OverflowException for example
                // It's source of confusion ( of other kind)
                ReturnValue = -2;
            }

            return ReturnValue;
        }

    }

The output in the console is the following :

Begin of Safe code
irv1 , Right is different from Wrong ! Do you expect something else?
irv2 , Right is different from Wrong ! Do you expect something else?
irv3 , Right is different from Wrong ! Do you expect something else?
End of Safe code
Begin of UnSafe code
irv1 , Confusion in progress!!
irv2 , Confusion in progress!!
irv3 , Confusion in progress!!
End of UnSafe code

Here It’s clear the potential damage that an ambiguous return value can do. After a brief research on internet it’s easy find a plethora of example where a simple unsafe mean() function can produce ambiguity. Ok , someone can say that a mean() function must return a double/decimal , but the problem will be the same , we will need a DoubleReturnValue to avoid ambiguity.

3. T4 at work

Now with T4 is possible write class for other primitive type in C# and for List. T4 aka Text Template Transformation Toolkit is a powerful tool to write repetitive code and here there is the code to begin to develop a custom class which implements some defensive issue.

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<# String[] ClassNames = new string [] {"String", 
                                            "int", 
                                            "Int16", 
                                            "UInt16",
                                            "Int32",
                                            "UInt32", 
                                            "Int64",
                                            "UInt64",
                                            "Byte",
                                            "SByte",
                                            "Single",
                                            "Double",
                                            "Char",
                                            "Boolean",
                                            "Object",
                                            "Decimal"
                                            }; #>
// Return Value
using System;
using System.Collections.Generic;
namespace ReturnValueN
{
    public abstract class ReturnValueAC
    {
        protected Boolean proSuccess = false;
        
        public void SetFalse()
        {
            proSuccess = false;
        }

        public Boolean Success
        {
            get {return proSuccess;}
        }
    }
namespace PrimitivesN
{
    <#  
    foreach (String classname in ClassNames) 
    { 
    #>

    /// <summary>
      /// 
      /// </summary>
    public class <#= classname #>ReturnValue : ReturnValueAC
    {
        private <#= classname #> priOutcome = default(<#= classname #>);

        public <#= classname #>ReturnValue()
        {

        }

        public <#= classname #>ReturnValue(<#= classname #> outcome , Boolean success)
        {
            priOutcome = outcome;
            proSuccess = success;
        }

        public <#= classname #> Outcome
        {
            get {return priOutcome;}
        }
    }

      <# 
      } 
      #>
} // End Namespace PrimitivesN
namespace ListOfN
{
    <#  
    foreach (String classname in ClassNames) 
    { 
    #>

    /// <summary>
      /// 
      /// </summary>
    public class ListOf<#= classname #>ReturnValue : ReturnValueAC
    {
        private List<<#= classname #>> priOutcome = new List<<#= classname #>>();

        public ListOf<#= classname #>ReturnValue()
        {

        }

        public ListOf<#= classname #>ReturnValue(List<<#= classname #>> outcome , Boolean success)
        {
            priOutcome = outcome;
            proSuccess = success;
        }

        public List<<#= classname #>> Outcome
        {
            get {return priOutcome;}
        }
    }
      <# 
      } 
      #>
} // End Namespace PrimitivesN

} // End of namespace

4. Updating Suggestions

The above T4 code contains a template for writing a Namespace of List, but nothing prohibit you to add some feature. Here I suggest some :

  • Namespace of Array
  • Namespace of other .NET class ( System.IO.File , XML , etc)
  • Class with log or time stamp.When something goes wrong it’s better know who did it or where\when happend. 

5. Reading

When i begin to write this article I thought it was a good thing add a chapter about interesting reading to learn how to do, not to do and so on. But after a Google research ( “defensive programming” for example ) i found  so much material that is’ hard to select something not too much language specific nor too much academic. So I decide to make a summary instead to write a long list of web page :

  • Check input parameter for range and type  : assert , code by contract ,specific function ,  etc
  • Check output parameter for range and type : assert , code by contract ,specific function ,  etc
  • Check early , check often
  • Reduce complexity : i prefer use temporary variable for example :
C#
if ( (a + b < c) || ( b < a  ) || ( a > 0) )
{
    // do something
}

Can be rewritten in :
Boolean bCheck1 = (a + b < c) ;
Boolean bCheck2 = ( b < a ) ;
Boolean bCheck3 = ( a > 0) ;
Boolean bCheckAll =  bCheck1  || bCheck2  || bCheck3 ;
if(bCheckAll  == true)
{
    // do something
}

In this way is easier to debug. Generally speaking always try to write a piece of code thinking that will have to be debugged. Do not assume that will work at the first attempt.

  • Source code review : allow others read the code.They will find bug faster.
  • Test it (automate tests are better)
  • Reuse it for others project , if it’s all ok you did a good job otherwise will find bugs because a different path in within the code produce different result.Fix it and go on. 

6. Conclusion

This article tackled defensive programming specifically for the issue of return function value with the little support of a tool like T4. It has the aim to provide some tips and suggestions on how to manage a typical programming problem. Help me a lot, hope help reader too.

History

First version : 2014 may 31

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Question[My vote of 1] This is no different than returning special values like null, -1, false or GetLastError Pin
Member 108452328-May-15 12:21
Member 108452328-May-15 12:21 
AnswerRe: [My vote of 1] This is no different than returning special values like null, -1, false or GetLastError Pin
ugo.marchesini9-May-15 14:54
professionalugo.marchesini9-May-15 14:54 
GeneralI agree but with generics Pin
fpw2323-Jun-14 3:24
fpw2323-Jun-14 3:24 
AnswerRe: I agree but with generics Pin
ugo.marchesini23-Jun-14 15:31
professionalugo.marchesini23-Jun-14 15:31 
General[My vote of 2] Generics? Pin
andykernahan3-Jun-14 5:53
andykernahan3-Jun-14 5:53 
AnswerRe: [My vote of 2] Generics? Pin
ugo.marchesini3-Jun-14 8:32
professionalugo.marchesini3-Jun-14 8:32 
GeneralMy vote of 2 Pin
LostTheMarbles2-Jun-14 21:59
professionalLostTheMarbles2-Jun-14 21:59 
GeneralRe: My vote of 2 Pin
ugo.marchesini2-Jun-14 23:47
professionalugo.marchesini2-Jun-14 23:47 
GeneralRe: My vote of 2 Pin
LostTheMarbles3-Jun-14 0:08
professionalLostTheMarbles3-Jun-14 0:08 
GeneralRe: My vote of 2 Pin
jeffb423-Jun-14 5:58
jeffb423-Jun-14 5:58 
QuestionCode block Pin
Nelek1-Jun-14 11:17
protectorNelek1-Jun-14 11:17 
AnswerRe: Code block Pin
ugo.marchesini1-Jun-14 13:17
professionalugo.marchesini1-Jun-14 13:17 
GeneralRe: Code block Pin
Nelek1-Jun-14 21:52
protectorNelek1-Jun-14 21:52 
QuestionWhich one? Pin
Member 1085659631-May-14 9:17
Member 1085659631-May-14 9:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.