143 Chapter 6 Debugging with Visual Studio 144 Microsoft Visual Studio 2010: A Beginner’s Guide Key Skills & Concepts ● Exploring Available Debugging Tools ● Setting Breakpoints ● Inspecting Program State ● Solving Problems with VS Debugging Tools M ore often than we would like, our code has bugs. Fortunately, when bugs do happen, you have a lot of help with VS. This chapter shows you how to use the VS debugger to fix problems by setting breakpoints, stepping through code, and inspecting program state. There’s also a section on development-time tools to inspect the structure of your code. Beyond setting breakpoints, you’ll learn how to customize breakpoints and how to manage a list of breakpoints. Then you’ll see the options VS has for stepping through code. This chapter also shows you many ways to see what the values of variables are in your code and the various tools available for inspecting your code. First, we’ll start with some example code you can use to practice the concepts learned in this chapter. Example Code for This Chapter It would take many pages of code to show a complete program with all of the complexity of a real-world scenario, which might be hard to follow for the purposes of this chapter. So, the example you’ll see simulates the environment of a full application. When performing debugging, you’ll need to traverse hierarchies in code, where one method calls another, which could go multiple levels deep, depending on the program. The example code will have multiple levels of method calls so that you can see how to use VS to debug code. Listing 6-1 shows the example code for this chapter. It’s a console application, just like all of the applications created in previous chapters. You create a console project by selecting File | New | Project, select the Console Application project, give the project a name, and generate the project by clicking OK. The application in Listing 6-1 calculates a discount for a customer, based on a special discount percentage for that customer and what that customer ordered. Chapter 6: Debugging with Visual Studio 145 Listing 6-1 Example code for chapter C#: Program.cs using System; namespace DebugAndTestDemo { class Program { static void Main() { Customer cust = new Customer(); cust.Discount = .1m; Order ord = new Order(); ord.AddItem(5.00m); ord.AddItem(2.50m); cust.Order = ord; decimal discount = cust.GetOrderDiscount(); Console.WriteLine("Customer Discount: {0}", discount); Console.ReadKey(); } } } C#: Customer.cs namespace DebugAndTestDemo { class Customer { public decimal Discount { get; set; } public Order Order { get; set; } public decimal GetOrderDiscount() { return Order.Total * Discount; } } } C#: Order.cs using System.Collections.Generic; 146 Microsoft Visual Studio 2010: A Beginner’s Guide namespace DebugAndTestDemo { class Order { private List<decimal> orderItems = new List<decimal>(); public decimal Total { get { decimal amount = 0; foreach (var item in orderItems) { amount = amount + item; } return amount; } } public void AddItem(decimal amount) { orderItems.Add(amount); } } } VB: Module1.vb Module Module1 Sub Main() Dim cust As Customer = New Customer() cust.Discount = 0.1D Dim ord As Order = New Order() ord.AddItem(5D) ord.AddItem(2.5D) cust.Order = ord Dim discount As Decimal = cust.GetOrderDiscount() Console.WriteLine("Customer Discount: {0}", discount) Console.ReadKey() End Sub End Module Chapter 6: Debugging with Visual Studio 147 VB: Customer.vb Class Customer Property Discount As Decimal Property Order As Order Function GetOrderDiscount() As Decimal Return Order.Total * Discount End Function End Class VB: Order.vb Class Order Private orderItems As New List(Of Decimal) Public ReadOnly Property Total() As Decimal Get Dim amount As Decimal = 0 For Each item In orderItems amount = amount + item Next Return amount End Get End Property Sub AddItem(ByVal item As Decimal) orderItems.Add(item) End Sub End Class A quick look at the code in Listing 6-1 tells you that this program is more sophisticated than the examples you’ve encountered in previous chapters. To understand what is happening, start at the Main method, the entry point of the application. There are two objects instantiated in Main, namely Customer and Order. After instantiating Customer, you can see that the Discount property on cust is being set to .1 (10%). This means that each instance of Customer can have a unique discount amount, which could be useful if you wanted to reward good shopping habits. Next, you can see the instantiation of Order and subsequent calls to AddItem on the object reference ord. This code only adds the order amount, but in a real scenario it would likely be a class with more fields to carry the specific details of the order item. The Customer class has an Order property, which the code then passes our Order instance, ord, to. Now, you have a Customer with a discount amount and it has a reference to our specific Order, which in turn has items (represented here by the items’ monetary amount only for brevity). 148 Microsoft Visual Studio 2010: A Beginner’s Guide This program calculates the total monetary discount that a customer would receive for that order by calling the GetOrderDiscount method on the Customer instance, which then returns the calculated discount amount to be subsequently displayed on the console. Essentially, we created a couple of object instances, cust and ord, gave the object instances the data they needed, and told the object instances to do some work for us. The result is a special discount monetary amount for a given customer, based on the customer’s items ordered. All of the code in the Main method is at the first level of the call hierarchy. The methods and properties in Customer and Order are at the second level of the hierarchy. Looking at Order, you can see that there is a Total property and an AddItem method. AddItem adds the item parameter to its orderItems collection. Total iterates through the orderItems collection, first calculating then returning the sum of all items. Notice that the Customer class has a Discount property that holds a decimal value that will be used as a percentage. The GetOrderDiscount method in Customer multiplies the Discount by the Total in Order to return the discount of the order. It’s important for you to study this example and understand the relationships and communication between various objects. Observe that each class has a distinct purpose, relating to how it is named. The purpose of the class helps decide what data and methods that class will have; Order has Total and AddItem, and the class Customer has Discount and GetOrderDiscount. Each object communicates with other objects, cooperating to perform a task. For example, it is Customer’s responsibility to calculate a discount because the Customer class knows what the discount should be (because we told it what the discount was in Main). However, Customer must communicate with Order because Order is the only object that knows about the order items and how to calculate the total. Although I’ve shown you the code and explained how it works, it’s often useful to see the flow of logic of the actual running program yourself. VS includes various visualization and debugging tools that help you understand the flow of logic, which are discussed next. Development-Time Code Tools One of the new features of VS 2010 is Call Hierarchy, which allows you to see what code calls a method and which methods are being called by your code. First, I’ll explain why call hierarchy is important, and then I’ll show you how to use it. Figure 6-1 shows what the Call Hierarchy window looks like, and the following discussion will explain the motivation for and use of the Call Hierarchy feature. The call hierarchy tells you several things about code, including the degree of reuse, impact of a change, and potential importance of a routine. To help understand the discussion, a call site is code that invokes another class member. For example, in Listing 6-1, the Main method is the call site and the GetOrderDiscount method is the called code. Chapter 6: Debugging with Visual Studio 149 From the perspective of reuse, many call sites to a method could indicate that the method is relatively generic and reusable. While a low number of call sites might not indicate the reusability of a method, zero call sites certainly indicates that the method is not being used and can potentially be eliminated. A lot of call sites could also indicate that a change to a method can have a significant impact. Looking at the number of call sites that a method has could be informative from the perspective of passing different values or seeing how many changes will be required in called methods. The previous discussion is to help you understand how call hierarchy might be useful. Now, let’s look at how call hierarchy works. First, remember that call hierarchy is context-sensitive, meaning that whatever code in the editor has focus defines your point of view. The point of view for this example will be the GetOrderDiscount method in the Customer class, and we want to see the call sites of GetOrderDiscount and what statements inside of GetOrderDiscount are call sites. To use call hierarchy, either right- click the GetOrderDiscount method in the editor and select View Call Hierarchy, or select GetOrderDiscount in the editor and press CTRL-K, T. VS shows the Call Hierarchy window in Figure 6-1. The Call Hierarchy window in Figure 6-1 shows Calls To and Calls From for the GetOrderDiscount method. Calls To is a list of call sites to the GetOrderDiscount method. Calls From is a list of statements within GetOrderDiscount that are call sites for other class members. The drop-down list at the top left of Figure 6-1, with My Solution selected, identifies how far Call Hierarchy will look to find Calls To and Calls From call sites. The options are My Solution, Current Project, and Current Document, which are self-explanatory. Figure 6-1 The Call Hierarchy window 150 Microsoft Visual Studio 2010: A Beginner’s Guide If you’ve been working on your code and want to update the Call Hierarchy window, click Refresh. Every time you view Call Hierarchy, the selected item is added to the list. You can use the Remove Root button to delete an item from the list. The Toggle Details Pane button shows and hides the Details pane, which shows the code and location of the call site. In Figure 6-1, the Main method is selected, which shows the call to GetOrderDiscounts off the cust instance of Customer from Listing 6-1. The actual code line is shown also. You can double-click the statement to navigate the editor to the location of that statement. In fact, you can double-click any call site in the Call Hierarchy to navigate to the location of the call site in the editor. The Call Hierarchy shows all of the possible paths you can take through a specific point in code. While quite useful, it’s limited to providing a static view of your code, and it does not provide the detailed insight into your running program that debugging may require. When debugging, you typically need to view the running state of an application at a specific point in time. The following sections show you various features of the debugger that help you inspect the runtime behavior of code. Configuring Debug Mode By default, VS creates projects with Debug mode enabled, which specifies project settings that make it possible for you to debug your application. The VS toolbar shows you the current configuration settings you’re using; clicking the drop-down list will show Debug and Release configurations. The Release configuration defines settings for your program that you want to use when you deploy it for production (actual) use. Y ou can also create a custom configuration that allows you to set project properties how you want. For the purposes of this chapter, we will use the Debug configuration. To understand what the Debug configuration gives you, ensure that the Debug configuration is selected in the toolbar; you’ll need to have a project open to do this. Then double-click the properties folder of your project and click the Build tab as shown in Figure 6-2. Figure 6-2 shows that optimizations are turned off and both TRACE and DEBUG are defined. Figure 6-2 shows the properties for a C# project, but in VB, the tab is called Compile. When optimizations are turned on, the compiler will perform extra processing on the code that makes it smaller and faster, altering the structure of the code. When debugging, you don’t want optimizations because you need the code you’re stepping through to match what the compiler produces. Compiler constants (also known as compiler directives) such as TRACE and DEBUG are used by the compiler to enable or disable blocks of code. For example, the System.Diagnostics namespace has a Debug class that will only work if DEBUG is defined. . 143 Chapter 6 Debugging with Visual Studio 144 Microsoft Visual Studio 2010: A Beginner’s Guide Key Skills & Concepts ● Exploring Available Debugging. * Discount; } } } C#: Order.cs using System.Collections.Generic; 146 Microsoft Visual Studio 2010: A Beginner’s Guide namespace DebugAndTestDemo { class Order { private List<decimal>. items (represented here by the items’ monetary amount only for brevity). 148 Microsoft Visual Studio 2010: A Beginner’s Guide This program calculates the total monetary discount that a customer