- •Contents
- •What Is C#?
- •C# Versus Other Programming Languages
- •Preparing to Program
- •The Program Development Cycle
- •Your First C# Program
- •Types of C# Programs
- •Summary
- •Workshop
- •C# Applications
- •Basic Parts of a C# Application
- •Structure of a C# Application
- •Analysis of Listing 2.1
- •Object-Oriented Programming (OOP)
- •Displaying Basic Information
- •Summary
- •Workshop
- •Variables
- •Using Variables
- •Understanding Your Computer’s Memory
- •C# Data Types
- •Numeric Variable Types
- •Literals Versus Variables
- •Constants
- •Reference Types
- •Summary
- •Workshop
- •Types of Operators
- •Punctuators
- •The Basic Assignment Operator
- •Mathematical/Arithmetic Operators
- •Relational Operators
- •Logical Bitwise Operators
- •Type Operators
- •The sizeof Operator
- •The Conditional Operator
- •Understanding Operator Precedence
- •Converting Data Types
- •Understanding Operator Promotion
- •For Those Brave Enough
- •Summary
- •Workshop
- •Controlling Program Flow
- •Using Selection Statements
- •Using Iteration Statements
- •Using goto
- •Nesting Flow
- •Summary
- •Workshop
- •Introduction
- •Abstraction and Encapsulation
- •An Interactive Hello World! Program
- •Basic Elements of Hello.cs
- •A Few Fundamental Observations
- •Summary
- •Review Questions
- •Programming Exercises
- •Introduction
- •Essential Elements of SimpleCalculator.cs
- •A Closer Look at SimpleCalculator.cs
- •Simplifying Your Code with Methods
- •Summary
- •Review Questions
- •Programming Exercises
- •Introduction
- •Lexical Structure
- •Some Thoughts on Elevator Simulations
- •Concepts, Goals and Solutions in an Elevator Simulation Program: Collecting Valuable Statistics for Evaluating an Elevator System
- •A Deeper Analysis of SimpleElevatorSimulation.cs
- •Class Relationships and UML
- •Summary
- •Review Questions
- •Programming Exercises
- •The Hello Windows Forms Application
- •Creating and Using an Event Handler
- •Defining the Border Style of the Form
- •Adding a Menu
- •Adding a Menu Shortcut
- •Handling Events from Menus
- •Dialogs
- •Creating Dialogs
- •Using Controls
- •Data Binding Strategies
- •Data Binding Sources
- •Simple Binding
- •Simple Binding to a DataSet
- •Complex Binding of Controls to Data
- •Binding Controls to Databases Using ADO.NET
- •Creating a Database Viewer with Visual Studio and ADO.NET
- •Resources in .NET
- •Localization Nuts and Bolts
- •.NET Resource Management Classes
- •Creating Text Resources
- •Using Visual Studio.NET for Internationalization
- •Image Resources
- •Using Image Lists
- •Programmatic Access to Resources
- •Reading and Writing RESX XML Files
- •The Basic Principles of GDI+
- •The Graphics Object
- •Graphics Coordinates
- •Drawing Lines and Simple Shapes
- •Using Gradient Pens and Brushes
- •Textured Pens and Brushes
- •Tidying up Your Lines with Endcaps
- •Curves and Paths
- •The GraphicsPath Object
- •Clipping with Paths and Regions
- •Transformations
- •Alpha Blending
- •Alpha Blending of Images
- •Other Color Space Manipulations
- •Using the Properties and Property Attributes
- •Demonstration Application: FormPaint.exe
- •Why Use Web Services?
- •Implementing Your First Web Service
- •Testing the Web Service
- •Implementing the Web Service Client
- •Understanding How Web Services Work
- •Summary
- •Workshop
- •How Do Web References Work?
- •What Is UDDI?
- •Summary
- •Workshop
- •Passing Parameters and Web Services
- •Accessing Data with Web Services
- •Summary
- •Workshop
- •Managing State in Web Services
- •Dealing with Slow Services
- •Workshop
- •Creating New Threads
- •Synchronization
- •Summary
- •The String Class
- •The StringBuilder Class
- •String Formatting
- •Regular Expressions
- •Summary
- •Discovering Program Information
- •Dynamically Activating Code
- •Reflection.Emit
- •Summary
- •Simple Debugging
- •Conditional Debugging
- •Runtime Tracing
- •Making Assertions
- •Summary
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