Скачиваний:
64
Добавлен:
15.03.2015
Размер:
4.31 Mб
Скачать

594

Extreme C#

PART IV

Prior to invoking code, a type instance is created by calling the GetType() method of the TypeBuilder object. The resulting Type object is then instantiated with the static

Activator.CreateInstance() method.

Once an object instance is available, the program gets a MethodInfo object and dynamically invokes the method, just like in the last section.

What’s really cool about this entire procedure is that you can save the work that was done to a file. With the Assembly object, the SetEntryPoint() method is invoked with the MethodInfo parameter for the dynamically generated Main() method. Then the file is saved with the Save command, which accepts a single string parameter specifying the assembly file name.

Warning

One of the goals of Listing 28.6 was to create an executable console application. For a C# program to run standalone, it must have a Main() method. Since the program did have a Main() method, it would be easy to assume that everything was good to go. However, the SetEntryPoint() method of the AssemblyBuilder must still be called or else the program will not run standalone. Remember, the system libraries are cross-language compatible, and you shouldn’t make the assumption that they know C#.

The Save() method of the AssemblyBuilder object creates two files. One file is the module named emitter.netmodule. This file can be compiled with other modules to create an executable. The other file is the executable named emitter.exe. This is a standalone program that will execute when invoked from the command line.

Summary

Reflection provides the capability to discover information about a program at runtime. Pertinent program items that can be reflected upon include assemblies, modules, types, and other kinds of C# program elements.

Another feature of reflection is the capability to dynamically activate code at runtime. This is especially relevant to situations where late-bound operations are required. With reflection, any type of C# code can be loaded and invoked dynamically.

The Reflection.Emit API provides advanced features for dynamically creating assemblies. This feature could be used in tools such as scripting engines and compilers. Once the code is created, it can be dynamically invoked or saved to file for later use.

CHAPTER 31

Runtime

Debugging

IN THIS CHAPTER

• Simple Debugging 636

• Conditional Debugging 638

• Runtime Tracing 641

• Making Assertions 643

636

Extreme C#

PART IV

There are several situations where runtime debugging and tracing are desirable. Often it’s easy to turn on debugging in a program, let it run, and watch a console screen for specific printouts representing the state of the program during execution. This is a quick way of isolating system failures during development.

For critical code, it may be useful to install a runtime trace facility. This provides a means to capture real-time information on production code and interact with administrators or analysts on what could be causing a problem.

The system libraries have facilities for supporting runtime debugging and tracing. This includes attributes and switches for conditional debugging and multilevel conditions for controlling trace output. It’s also possible to monitor the logical implementation of code with assertions.

The System.Diagnostics namespace has two primary classes for runtime debugging: Debug and Trace. For the most part, their functionality is similar; the primary difference between the two comes from how they are used. The Debug class is strictly for development environments and requires a DEBUG directive or command-line option to be specified to activate its functionality. The Trace class is automatically activated and doesn’t require any directive or command-line options. This is because the Trace class is for programs to be deployed with debugging capability. Debugging code introduces overhead in a program. If programs should not be deployed with debugging information, which reduces overhead, use the Debug class. However, if there’s a need to have debugging information available in deployment and the overhead is acceptable, the Trace class does the trick.

Simple Debugging

In its simplest form, runtime debugging is just a matter of printing out statements to the console. The Debug class, a member of the System.Diagnostics namespace, has two methods for supporting explicit debugging: Write() and WriteLine(). These methods work similar to their Console class counterparts. Listing 31.1 shows an example that uses the WriteLine() method of the Debug class.

LISTING 31.1 A Simple Debugging Example: PlainDebugDemo.cs

#define DEBUG

using System;

using System.Diagnostics;

///<summary>

///Plain Debug Demo.

LISTING 31.1 continued

/// </summary>

class PlainDebugDemo

{

static void DebuggedMethod()

{

Debug.WriteLine(“Debug: Entered MyMethod()”);

}

static void Main(string[] args)

{

TextWriterTraceListener myListener =

new TextWriterTraceListener(Console.Out);

Debug.Listeners.Add(myListener);

DebuggedMethod();

}

}

Runtime Debugging

CHAPTER 31

637

31

UNTIMER

EBUGGINGD

And here’s the output:

Debug: Entered MyMethod()

Setting up a program for debugging requires statements to specify where debug output should be sent. The Main() method in Listing 31.1 creates a TextWriterTraceListener class that directs debugging output to the console window. It then adds the listener to the collection of Debug listeners.

Listing 31.1 used a TextWriter object, Console.out, as its output destination. However, debug output could have been just as well sent to a file by instantiating a Stream object and providing it as the parameter to the TextWriterTraceListener instantiation. The TextWriterTraceListener class also has methods to flush and close debug output with the Flush() and Close() methods, respectively.

The Listeners collection of the Debug class accepts any derived TraceListener class. Therefore, it’s possible to create customized trace listeners by deriving them from either the TraceListener or TextWriterTraceListener classes.

Once an output destination is set up, the program invokes the DebuggedMethod() method, which calls the WriteLine() method of the Debug class. This produces the output shown following the listing.

There are a couple ways to enable debugging. At the top of Listing 31.1 is a #define DEBUG directive, enabling the operation of the Debug class. Additionally, Listing 31.2 shows how to enable debugging with the command line option, /d:DEBUG. One or the