饭饭TXT > 学习管理 > 《Visual Studio 2010 高级编程(英文出书版)》作者:Nick Randolph/等【完结】 > [Visual.Studio.2010.高级编程].Professional.Visual.Studio.2010.txt

第 127 页

作者:Nick Randolph/等 当前章节:15397 字 更新时间:2026-6-18 14:51

that is called by that code, the attributed code

will be marked as external code in the

call stack. This is illustrated in Figure 41-6,

which shows the code that was listed in

the previous section. However, in this case

DebuggerStepThrough has been set on fiGure 41-6

HiddenMethod instead of DebuggerHidden.

Visual Studio 2010 supports the Just My Code option, configurable from the Debugging node in

the Options dialog (select Tools . Options). Unchecking this option makes all code contained

within your application appear in the call

stack, as shown in Figure 41-7. This includes

designer and other generated code that you

might not want to debug. Once this option

is unchecked, breakpoints can also be set in

blocks of code marked with this attribute.

fiGure 41-7

You can also right-click the call stack and select “Show External Code” to reveal

any hidden or designer code.

debuggernonusercode

The DebuggerNonUserCode attribute combines the DebuggerHidden and DebuggerStepThrough

attributes. In the default Visual Studio configuration, code marked with this attribute appears

as external code in the call stack. As was the case with the DebuggerStepThrough attribute, you

cannot set breakpoints in blocks of code marked with this attribute. Stepping through code steps

into any code called by that block of code in the same way it does for the DebuggerHidden

attribute.

debuggerstepperboundary

DebuggerStepperBoundary is the most obscure of all of the Debugger attributes, because it

comes into effect only under specific conditions. It is used to avoid a misleading debugging

experience that can occur when a context switch is made on a thread within the boundaries of the

Type Proxies .

863

DebuggerNonUserCode attribute. It is entirely possible in this scenario that the next user-supplied

code module stepped into may not actually relate to the code that was in the process of being

debugged. To avoid this invalid debugging behavior, the DebuggerStepperBoundary attribute,

when encountered under this scenario, will escape from stepping through code and instead resume

normal execution of the code.

tyPe Proxies

So far, you have seen how you can modify the tooltip to show information that is more relevant

to debugging your application. However, the attributes discussed so far have been limited in how

they control what information is presented in the expanded tree. The DebuggerBrowsable attribute

enables you to hide particular members, but there is no way to add more fields. This is where the

DebuggerTypeProxy attribute can be used to provide you with complete control over the layout of

the tooltip.

The other scenario where a type proxy is useful is where a property of a class changes values

within the class. For example, the following snippet from the Customer class tracks the number

of times the OrderCount property has been accessed. Whenever the tooltip is accessed, the

CountAccessed property is incremented by one:

c#

public class Customer

{

private int m_CountAccessed;

public int OrderCount

{

get

{

m_CountAccessed++;

return this.Orders.Count;

}

}

public int CountAccessed

{

get

{

return this.m_CountAccessed;

}

}

}

Figure 41-8 illustrates the tooltip you want to be shown for

the Customer class. Instead of showing the full list of orders to

navigate through, it provides a summary about the number of

orders, the maximum and minimum order quantities, and a list

of the items on order.

The first line in the tooltip is the same as what you created using

the DebuggerDisplay attribute. To generate the rest of the tooltip,

fiGure 41-8

864 .

chaPter 41 dATATipS, debug proxieS, And ViSuAlizerS

you need to create an additional class that will act as a substitute when it comes to presenting

this information. You then need to attribute the Customer class with the DebuggerTypeProxy

attribute so the debugger knows to use that class instead of the Customer class when displaying the

tooltip. The following code snippet shows the CustomerProxy class that has been nested within

the Customer class:

c#

[DebuggerDisplay("Customer {CustomerName} has {Orders.Count} orders")]

[DebuggerTypeProxy(typeof(Customer.CustomerProxy))]

public class Customer

{

private int m_CountAccessed;

public int OrderCount

{

get

{

m_CountAccessed++;

return this.Orders.Count;

}

}

public int CountAccessed

{

get

{

return this.m_CountAccessed;

}

}

public class CustomerProxy

{

public string CustomerName;

public int NumberOfOrders;

public decimal MaximumTotal = decimal.MinValue;

public decimal MinimumTotal = decimal.MaxValue;

public CustomerProxy(Customer c)

{

this.CustomerName = c.m_CustomerName;

this.NumberOfOrders = c.m_Orders.Count;

foreach (Order o in c.m_Orders)

{

this.MaximumTotal = Math.Max(o.Total, this.MaximumTotal);

this.MinimumTotal = Math.Min(o.Total, this.MinimumTotal);

}

}

}

}

There are very few reasons why you should create public nested classes, but a type proxy is a good

example because it needs to be public so it can be specified in the DebuggerTypeProxy attribute,

and it should be nested so it can access private members from the Customer class without using the

public accessors.

Visualizers .

865

raw View

On occasion, you might want to ignore the proxy type. For example, this might be true if you

are consuming a third-party component that has a proxy type defined for it that disguises the

underlying data structure. If something is going wrong with the way the component is behaving,

you might need to review the internal contents of the component to trace the source of the issue.

In Figure 41-8, you may have noticed at the bottom of the tooltip was a node titled Raw View.

Expanding this node displays the debugger tooltip as it is normally shown, without any proxy types

or debugger display values.

In addition, you can turn off all type proxies in Visual Studio through the Tools . Options menu.

Under the Debugging node, check the box that says Show Raw Structure of Objects in Variables

Windows. Doing this prevents all type proxies and debugger displays from being shown.

Visualizers

This part of the chapter looks at a feature in Visual Studio 2010 that can be used to help debug

more complex data structures. Two of the most common data types programmers work with are

Strings and DataTables. Strings are often much larger than the area that can be displayed within a

tooltip, and the structure of the DataTable

object is not suitable for displaying in a

tooltip, even using a type proxy. In both of

these cases, a visualizer has been created that

enables the data to be viewed in a sensible

format.

Once a visualizer has been created for a

particular type, a magnifying glass icon

appears in the first line of the debugger

tooltip. Clicking this icon displays the

visualizer. Figure 41-9 shows the Text

Visualizer dialog that appears.

Before you can start writing a visualizer, you need to add a reference to the Microsoft.

VisualStudio.DebuggerVisualizers namespace. To do this, right-click the project in the Solution

Explorer and select Add Reference from the context menu. You should also add this namespace as

an import to any classes for which you plan to create debugger visualizers.

fiGure 41-9

The version of Microsoft.VisualStudio.DebuggerVisualizers that ships

with Visual Studio 2010 is valid only for projects that target version 4.0 of the

Microsoft .NET Framework.

866 .

chaPter 41 dATATipS, debug proxieS, And ViSuAlizerS

A visualizer is typically made up of two parts: the class that acts as a host for the visualizer and

is referenced by the DebuggerVisualizer attribute applied to the class being visualized, and

the form that is then used to display, or visualize, the class. Figure 41-10 shows a simple form,

CustomerForm, which can be used to represent the customer information. This is just an ordinary

Windows Form with a couple of TextBox controls, a DataGridView control, and a Button. The

only unique aspect to this form is that it has been marked as Serializable, and its constructor has

been changed to accept a Customer object, from which the customer information is extracted and

displayed, as shown in the following code:

c#

[Serializable()]

public partial class CustomerForm : Form

{

public CustomerForm(Customer c)

{

InitializeComponent();

this.txtCustomerId.Text = c.CustomerId.ToString();

this.txtCustomerName.Text = c.CustomerName;

this.dgOrders.DataSource = c.Orders;

}

private void btnOk_Click(object sender, EventArgs e)

{

this.DialogResult = DialogResult.OK;

this.Close();

}

}

The next stage is to wire this form up to

be used as the visualizer for the Customer

class. You do this by creating the nested

CustomerVisualizer class, which inherits

from the DialogDebuggerVisualizer

abstract class, as shown in the following

code:

c#

[Serializable()]

[DebuggerDisplay("Customer {CustomerName} has {Orders.Count} orders")]

[DebuggerTypeProxy(typeof(Customer.CustomerProxy))]

[DebuggerVisualizer(typeof(Customer.CustomerVisualizer))]

public class Customer

{

//...

public class CustomerVisualizer : DialogDebuggerVisualizer

{

protected override void Show(

IDialogVisualizerService windowService,

IVisualizerObjectProvider objectProvider)

fiGure 41-10

advanced Techniques .

867

{

Customer c = (Customer)objectProvider.GetObject();

CustomerForm cf = new CustomerForm(c);

windowService.ShowDialog(cf);

}

}

}

Unlike the type proxy, which interacts with the actual Customer object being debugged, visualizers

need to be able to serialize the class being debugged so the class can be moved from the process

being debugged to the process that is doing the debugging, and will subsequently be shown in the

visualizer. As such, both the Customer and Order classes need to be marked with the Serializable

attribute.

The Show method of the CustomerVisualizer class does three things. To display the Customer

object being debugged, first you need to get a reference to this object. You do this via the GetObject

method on the ObjectProvider object. Because the communication between the two processes is

done via a stream, this method does the heavy lifting associated with deserializing the object so you

can work with it.

Next you need to pass the Customer object to a new instance of the CustomerForm. Finally, use

the ShowDialog method on the WindowService object to display the form. It is important that you

display the form using this object because it will ensure that the form is displayed on the appropriate

UI thread.

Lastly, note that the CustomerVisualizer class is referenced in the DebuggerVisualizer attribute,

ensuring that the debugger uses this class to load the visualizer for Customer objects.

As a side note, if you write components and want to ship visualizers separately from the components

themselves, visualizers can be installed by placing the appropriate assembly into either the C:\

Program Files\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\Visualizers

directory (Program Files (x86) on 64-bit Windows), or the Documents\Visual Studio 2010\

Visualizers directory.

adVanced techniques

Thus far, this chapter has covered how to display and visualize objects you are debugging. In earlier

chapters, you learned how to modify field and property values on the object being debugged via the

data tip. The missing link is being able to edit more complex data objects. The final section in this

chapter looks at how to extend your visualizer so you can save changes to the Customer object.

saving changes to your object

When you created the CustomerVisualizer, you had to retrieve the Customer object from

the communication stream using the GetObject method. This essentially gave you a clone of the

Customer object being debugged to use with the visualizer. To save any changes you make in

the CustomerVisualizer, you need to send the new Customer object back to the process being

debugged. You can do this using the ReplaceObject method on the ObjectProvider, which gives

you a CustomerVisualizer.

868 .

chaPter 41 dATATipS, debug proxieS, And ViSuAlizerS

Before you can call the ReplaceObject method you will need to make some changes to pass the

modified Customer object back to the visualizer. This has been done by saving the Customer object

to an internal variable when it is initially passed into the class, and exposing this variable via a read-

only property. This is shown in the following code:

c#

[Serializable()]

public partial class CustomerForm : Form

{

public CustomerForm(Customer c)

{

InitializeComponent();

this.txtCustomerId.Text = c.CustomerId.ToString();

this.txtCustomerName.Text = c.CustomerName;

this.dgOrders.DataSource = c.Orders;

m_ModifiedCustomer = c;

}

private Customer m_ModifiedCustomer;

public Customer ModifiedCustomer

{

get

{

m_ModifiedCustomer.CustomerId = new Guid(txtCustomerId.Text);

m_ModifiedCustomer.CustomerName = txtCustomerName.Text;

m_ModifiedCustomer.Orders = (List<Order>)dgOrders.DataSource;

return m_ModifiedCustomer;

}

}

private void btnOk_Click(object sender, EventArgs e)

{

this.DialogResult = DialogResult.OK;

this.Close();

}

}

You can now easily access the modified Customer object and save the changes back by calling the

ReplaceObject method as shown here:

c#

[Serializable()]

[DebuggerDisplay("Customer {CustomerName} has {Orders.Count} orders")]

[DebuggerTypeProxy(GetType(Customer.CustomerProxy))]

目录
设置
设置
阅读主题
字体风格
雅黑 宋体 楷书 卡通
字体大小
适中 偏大 超大
保存设置
恢复默认
手机
手机阅读
扫码获取链接,使用浏览器打开
书架同步,随时随地,手机阅读
首 页 < 上一章 章节列表 下一章 > 尾 页