Wednesday, September 23, 2015

[ C# ] - Debugger Attributes Explained with Examples

1. Introduction

In the last article, we saw about Logging and getting function call stack information. In this article, we will see “Debugger Attributes” which controls debugging ability and provide a rich experience to the debugging user. An "Attribute" is a Tag defined over the elements like Class, Functions, assemblies etc. These tags determine how the elements should behave at run time. Let us see below specified debugging attributes with a simple example:


  1. DebuggerBrowsable Attribute
  2. DebuggerDisplay Attribute
  3. DebuggerHidden Attribute


2. About the Example

The example used in this article is shown in the screenshot below:

DebuggerAttributes Fig.1
Fig 1. Debugger Attributes Example

In each button click handler, the debugger breakpoint is invoked dynamically and hence it is advised to launch the application through "VisualStudio" with F5 (i.e.) start the application through the menu option Debug->Start Debugging. Once you download this example application, watch the video which demonstrates the usage of each attribute.

3. The Book Class

First a class called Book is defined to demonstrate the debugging attributes. This class has three private members and constructor to initialize those private members. The code for the class is given below:

using System;
using System.Collections.Generic;
using System.Text;

namespace DebugAttrib
{
    //Sample 01: Default Book Class
    class Book
    {
        private int m_bookid;
        private String m_bookname;
        private String m_author;

        public Book(Int32 idno, String bookname, String Author)
        {
            m_bookid = idno;
            m_bookname = bookname;
            m_author = Author;
        }
    }
}


4. Default Behaviour of Book Class

The “Default Class” button click handler examines the behaviour of the above-specified book class. In the form file, to break into the code dynamically for debugging, the Diagnosis namespace is used. The Code is given below:

//Sample 02: Required NameSpace
using System.Diagnostics;

The “Default Class” button click handler creates the instance of Book in order to examine the default behaviour. The code is below:

//Sample 03: Class Default in Auto Window
private void btnDefault_Click(object sender, EventArgs e)
{
    Debugger.Break();
    Book bk = new Book(110, "C++ for Starters", "Rob Kati");
}

When you debug the class instance “bk”, you can see all the member information in the debugger window. Also, the debugger shows the Namespace and Class Name in the instance level. Have a look at the below video to know the default behaviour of the Book Class Instance:

Video 1: Default Book Class Behaviour



5. Book class behaviour with ToString Override

Once you override the "ToString() Method" in the class, the debugger knows how to translate the class in the string format. During the debug time, the ToString implementation provided by us will be invoked by the debugger. Have a look at the below code:

//Sample 04a: Override ToString
public override string ToString()
{
    return string.Format("{0}[{1}] by {2}", m_bookname, m_bookid, m_author);
}

In the above code, a string is formed based on the members present in the class and that string is returned to the caller.  In the debugger output, we can now see the meaningful information against the class Instance Name instead of the default "Namespace.ClassName" as it is already displayed under the "Type" column.


6. DebuggerBrowsable Attribute

There are various windows like Auto, Quick watch etc can browse the class information and examine the values in each member. The "DebuggerBrowsable Attribute" controls what information can be browse-able through the debugger. To demonstrate the debugger browsable attribute, the default Book class is shown in the previous section is modified and the modified version of the book class is Book1. Have a look at the Book1 class below:

//Sample 05: Copy Pasted Book class. Look @ 4.x for the Modifications
class Book1
{
    //Sample 5.1: Change all private member as public
    public int m_bookid;
    public String m_bookname;
    public String m_author;

    //Sample 5.2: Add a Private Member and Check Attribute
    // Sample 7.0: Add the Attrubute
    [ DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private String m_publisher;
   

    //Sample 5.3: Add new member to constructor
    public Book1(Int32 idno, String bookname, String Author, String publication)
    {
        m_bookid = idno;
        m_bookname = bookname;
        m_author = Author;

        //Sample 5.4: Initialize the Private Member
        m_publisher = publication;
    }

    //Sample 04b: Override ToString
    public override string ToString()
    {
        return string.Format("{0} by {1} from {2}", m_bookname, m_author, m_publisher);
    }
}

In the above code, notice the member “m_publisher” is marked with DebuggerBrowsable attribute. Here, we set "DebuggerBrowsableState.Never" through the DebuggerBrowsable attribute. The never state informs the runtime that the member should not be browse-able while debugging any instance of the class Book1. There are other browse-able states and those are listed below:
  1. Collapsed - Shows the element as collapsed.
  2. Never - Never show the element.
  3. RootHidden - Do not display the root element; display the child elements if the element is a collection or array of items.

In the Main form, “Browsable” button click handler is provided to test the class Book1. The code is given below:

//Sample 06: Debug and check Browsable attribute
private void btnBrowse_Click(object sender, EventArgs e)
{
    Debugger.Break();
    Book1 bk = new Book1(110, "C++ for Starters", "Rob Kati", "KPB");
}

Video 2: Browsable Attribute and effect of ToString Override



7. DebuggerDisplay Attribute

The debugger display attribute can be marked for elements like class, functions, properties etc. Consider the below screen shot:

DebuggerAttributes Fig.2
Fig 2. DebuggerDisplay Attribute

Here, the "DebuggerDisplay Attribute" is defined for the class member m_author.  The attribute is marked as 1 in the above picture and the attribute takes a string. Note that Debugger display string is accessing the class member within curly basis (Marked as 2). At runtime, the value will be substituted from the actual variable m_author (marked as 3). To examine the above attribute, the basic Book class is modified to have Book2 class. In Book2 class, all three data members are marked with DebuggerDisplay attribute. The code is shown below:


//Sample 08: Changed Class Name as Book2
class Book2
{
    //Sample 09: Add Attributes to Private Member
    [DebuggerDisplay("Book ID is {m_bookid}.")]
    private int m_bookid;

    [DebuggerDisplay("Book Title is {m_bookname}")]
    private String m_bookname;

    [DebuggerDisplay("Written by {m_author}")]
    private String m_author;

In the Main form, Click event for the button “Debugger Display” is handled. The event handler code is listed below:

//Sample 10: Debugger Display Attribute
private void btnDebuggerDisplay_Click(object sender, EventArgs e)
{
    Debugger.Break();
    Book2 bk = new Book2(110, "C++ for Starters", "Rob Kati");
}

Video 3: DebuggerDisplay Attribute in action



8. DebuggerHidden Attribute

When you mark a member function with this attribute the debugger will not stop on that method; that means you can’t keep a breakpoint on that method. To check this attribute, the Book2 class is modified to have a new method called GetPrice which returns different price based on the month-Range in a year. Since the GetPrice is marked with "DebuggerHidden Attribute" we can’t debug this particular function in the Book2 class. The code is given below:

//Sample 11: Get Price of the book
//Sample 13: Add the Hidden Attribute
[DebuggerHidden()]
public double GetPrice()
{
    DateTime today = DateTime.Now;
    if (today.Month > 0 && today.Month < 5)
        return 500.50;
    else if (today.Month > 4 && today.Month < 10)
        return 805.00;
    else
        return 200.20;
}

The “Hide Function” button click event handler creates the Instance of Book2 and makes a call to GetPrice. You can examine the function to see how debugging is prohibited for the GetPrice. Below is code for button click event handler:

//Sample 12: Debugger Hidden Attribute
private void btnDebuggerFnHide_Click(object sender, EventArgs e)
{
    Debugger.Break();
    Book2 bk = new Book2(110, "C++ for Starters", "Rob Kati");
    double price = bk.GetPrice();
    Debugger.Log(0, "Information", string.Format("Price={0}", price));
}


Video 4: Checking the DebuggerHidden Attribute





Source code : Download

No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Firend :)