However, keep inmind that in a real search, you would probably use only a part of the source stringrather than the entire source string, as shown here: // group time = one or more digits
Trang 1Example 10-6 is identical to Example 10-5, except that the latter example doesn’tinstantiate an object of typeRegex Instead, Example 10-6 uses the staticversion ofSplit( ), which takes two arguments: a string to search for, and a regular expressionstring that represents the pattern to match.
The instance method ofSplit( )is also overloaded with versions that limit the ber of times the split will occur as well as determine the position within the targetstring where the search will begin
num-Using Regex Match Collections
Two additional classes in the NET RegularExpressions namespace allow you tosearch a string repeatedly, and to return the results in a collection The collectionreturned is of type MatchCollection, which consists of zero or more Match objects.Two important properties of aMatchobject are its length and its value, each of whichcan be read as illustrated in Example 10-7
string s1 = "One,Two,Three Liberty Associates, Inc.";
StringBuilder sBuilder = new StringBuilder( );
string string1 = "This is a test string";
Example 10-6 Using static Regex.Split( ) (continued)
Trang 2Example 10-7 creates a simple string to search:
string string1 = "This is a test string";
and a trivial regular expression to search it:
Regex theReg = new Regex(@"(\S+)\s");
The string \S finds nonwhitespace, and the plus sign indicates one or more Thestring\s(note lowercase) indicates whitespace Thus, together, this string looks forany nonwhitespace characters followed by whitespace
Remember that the at (@) symbol before the string creates a verbatim
string, which avoids having to escape the backslash (\) character.
The output shows that the first four words were found The final word wasn’t foundbecause it isn’t followed by a space If you insert a space after the wordstring, andbefore the closing quotation marks, this program finds that word as well
// find any nonwhitespace followed by whitespace
Regex theReg = new Regex(@"(\S+)\s");
// get the collection of matches
MatchCollection theMatches = theReg.Matches(string1);
// iterate through the collection
foreach (Match theMatch in theMatches)
Trang 3Thelength property is the length of the captured substring, and I discuss it in thesection “Using CaptureCollection” later in this chapter.
Using Regex Groups
It is often convenient to group subexpression matches together so that you can parseout pieces of the matching string For example, you might want to match on IPaddresses and group all IP addresses found anywhere within the string
IP addresses are used to locate computers on a network, and typically
have the form x.x.x.x, where x is generally any digit between 0 and
string string1 = "04:03:27 127.0.0.0 LibertyAssociates.com";
// group time = one or more digits or colons followed by space
Regex theReg = new Regex(@"(?<time>(\d|\:)+)\s" +
// ip address = one or more digits or dots followed by space
@"(?<ip>(\d|\.)+)\s" +
// site = one or more characters
@"(?<site>\S+)");
Trang 4Again, Example 10-8 begins by creating a string to search:
string string1 = "04:03:27 127.0.0.0 LibertyAssociates.com";
This string might be one of many recorded in a web server logfile or produced as theresult of a search of the database In this simple example, there are three columns:one for the time of the log entry, one for an IP address, and one for the site, each sep-arated by spaces Of course, in an example solving a real-life problem, you mightneed to do more complex queries and choose to use other delimiters and more com-plex searches
In Example 10-8, we want to create a singleRegexobject to search strings of this typeand break them into three groups: time, ip address, andsite The regular expres-sion string is fairly simple, so the example is easy to understand However, keep inmind that in a real search, you would probably use only a part of the source stringrather than the entire source string, as shown here:
// group time = one or more digits or colons
// followed by space
Regex theReg = new Regex(@"(?<time>(\d|\:)+)\s" +
// ip address = one or more digits or dots
// get the collection of matches
MatchCollection theMatches = theReg.Matches(string1);
// iterate through the collection
foreach (Match theMatch in theMatches)
Trang 5The parentheses create a group Everything between the opening parenthesis (justbefore the question mark) and the closing parenthesis (in this case, after the+sign) is
a single unnamed group
The string ?<time> names that group time, and the group is associated with thematching text, which is the regular expression (\d|\:)+)\s This regular expressioncan be interpreted as “one or more digits or colons followed by a space.”
Similarly, the string?<ip>names theipgroup, and?<site>names thesitegroup AsExample 10-7 does, Example 10-8 asks for a collection of all the matches:
MatchCollection theMatches = theReg.Matches(string1);
Example 10-8 iterates through theMatches collection, finding eachMatch object
If the Length of theMatch is greater than0, a Match was found; it prints the entirematch:
In Example 10-8, theMatches collection has only oneMatch It is possible, however,
to match more than one expression within a string To see this, modifystring1 inExample 10-8 to provide severallogFile entries instead of one, as follows:
string string1 = "04:03:27 127.0.0.0 LibertyAssociates.com " +
"04:03:28 127.0.0.0 foo.com " +
"04:03:29 127.0.0.0 bar.com " ;
This creates three matches in the MatchCollection, called theMatches Here’s theresulting output:
Trang 6For eachMatch item found, you can print the entire match, various groups, or both.
Using CaptureCollection
Please note that we are now venturing into advanced use of regular expressions,which themselves are considered a black art by many programmers Feel free to skipover this section if it gives you a headache, and come back to it if you need it
Each time aRegexobject matches a subexpression, aCaptureinstance is created andadded to a CaptureCollection collection Each Capture object represents a singlecapture
Each group has its own capture collection of the matches for the subexpression ciated with the group
asso-So, taking that apart, if you don’t createGroups, and you match only once, you end
up with oneCaptureCollectionwith oneCaptureobject If you match five times, youend up with oneCaptureCollection with fiveCapture objects in it
If you don’t create groups, but you match on three subexpressions, you will end upwith three CaptureCollections, each of which will have Capture objects for eachmatch for that subexpression
Finally, if you do create groups (e.g., one group for IP addresses, one group formachine names, one group for dates), and each group has a few capture expressions,you’ll end up with a hierarchy: each group collection will have a number of capturecollections (one per subexpression to match), and each group’s capture collectionwill have a capture object for each match found
A key property of theCaptureobject is itslength, which is the length of the capturedsubstring When you askMatch for its length, it isCapture.Length that you retrievebecauseMatch derives fromGroup, which in turn derives fromCapture
Trang 7The regular expression inheritance scheme in NET allows Match to
include in its interface the methods and properties of these parent
classes In a sense, a Group is-a capture: it is a capture that
encapsu-lates the idea of grouping subexpressions A Match, in turn, is-a Group:
it is the encapsulation of all the groups of subexpressions making up
the entire match for this regular expression (See Chapter 5 for more
about the is-a relationship and other relationships.)
Typically, you will find only a singleCapturein aCaptureCollection, but that neednot be so Consider what would happen if you were parsing a string in which thecompany name might occur in either of two positions To group these together in asingle match, create the?<company>group in two places in your regular expressionpattern:
Regex theReg = new Regex(@"(?<time>(\d|\:)+)\s" +
string string1 = "04:03:27 Jesse 0.0.0.127 Liberty ";
The string includes names in both of the positions specified Here is the result:theMatch: 04:03:27 Jesse 0.0.0.127 Liberty
Example 10-9 Examining the Captures collection
// the string to parse
// note that names appear in both
Trang 8// searchable positions
string string1 =
"04:03:27 Jesse 0.0.0.127 Liberty ";
// regular expression that groups company twice
Regex theReg = new Regex(@"(?<time>(\d|\:)+)\s" +
// iterate through the collection
foreach (Match theMatch in theMatches)
// iterate over the captures collection
// in the company group within the
// groups collection in the match
foreach (Capture cap in
Trang 9The code in bold iterates through theCaptures collection for theCompany group:foreach (Capture cap in
theMatch.Groups["company"].Captures)
Let’s review how this line is parsed The compiler begins by finding the collectionthat it will iterate over.theMatchis an object that has a collection namedGroups TheGroupscollection has an indexer that takes a string and returns a singleGroupobject.Thus, the following line returns a singleGroup object:
theMatch.Groups["company"]
TheGroupobject has a collection namedCaptures Thus, the following line returns aCaptures collection for the Group stored at Groups["company"] within the theMatchobject:
theMatch.Groups["company"].Captures
The foreach loop iterates over the Captures collection, extracting each element inturn and assigning it to the local variablecap, which is of typeCapture You can seefrom the output that there are two capture elements:JesseandLiberty The secondone overwrites the first in the group, and so the displayed value is justLiberty How-ever, by examining the Captures collection, you can find both values that werecaptured
Trang 10Chapter 11 CHAPTER 11
Exceptions11
Like many object-oriented languages, C# handles abnormal conditions with
excep-tions An exception is an object that encapsulates information about an unusual
program occurrence
It is important to distinguish between bugs, errors, and exceptions A bug is a
pro-grammer mistake that should be fixed before the code is shipped Exceptions aren’t aprotection against bugs Although a bug might cause an exception to be thrown, youshould not rely on exceptions to handle your bugs Rather, you should fix the bugs
An error is caused by user action For example, the user might enter a number where
a letter is expected Once again, an error might cause an exception, but you can vent that by catching errors with validation code Whenever possible, errors should
pre-be anticipated and prevented
Even if you remove all bugs and anticipate all user errors, you will still run into able but unpreventable problems, such as running out of memory or attempting toopen a file that no longer exists You can’t prevent exceptions, but you can handlethem so that they don’t bring down your program
predict-When your program encounters an exceptional circumstance, such as running out of
memory, it throws (or “raises”) an exception When an exception is thrown,
execu-tion of the current funcexecu-tion halts, and the stack is unwound until an appropriateexception handler is found (see the sidebar, “Unwinding the Stack”)
This means that if the currently running function doesn’t handle the exception, thecurrent function will terminate, and the calling function will get a chance to handlethe exception If none of the calling functions handles it, the exception willultimately be handled by the CLR, which will abruptly terminate your program
An exception handler is a block of code designed to handle the exception you’ve
thrown Exception handlers are implemented ascatchstatements Ideally, if the tion is caught and handled, the program can fix the problem and continue Even ifyour program can’t continue, by catching the exception, you have an opportunity toprint a meaningful error message and terminate gracefully
Trang 11excep-If there is code in your function that must run regardless of whether an exception isencountered (e.g., to release resources you’ve allocated), you can place that code in afinally block, where it is certain to run, even in the presence of exceptions.
Throwing and Catching Exceptions
In C#, you can throw only objects of typeSystem.Exception, or objects derived fromthat type The CLR System namespace includes a number of exception types thatyour program can use These exception types include ArgumentNullException,InvalidCastException, andOverflowException, as well as many others
C++ programmers take note: in C#, not just any object can be
thrown—it must be derived from System.Exception.
The throw Statement
To signal an abnormal condition in a C# class, you throw an exception To do this,use the keywordthrow This line of code creates a new instance ofSystem.Exceptionand then throws it:
throw new System.Exception( );
Throwing an exception immediately halts execution of the current “thread” (seeChapter 21 for a discussion of threads) while the CLR searches for an exception han-dler If an exception handler can’t be found in the current method, the runtime
Unwinding the Stack
When a method is called, an area is set aside on the stack, known as the stack frame,
which holds the return address of the next instruction in the calling method, the ments passed into the called method, and all the local variables in the called method.BecauseMethodAcan callMethodBwhich can callMethodCwhich can, in fact, callMethodA
argu-(which can even callMethodA!), and so on, “unwinding the stack” refers to the process
of finding the return address of the calling method and returning to that methodperemptorily, looking for acatchblock to handle the exception The stack may have
to “unwind” through a number of called methods before it finds a handler Ultimately,
if it unwinds all the way tomainand no handler is found, a default handler is called,and the program exits
Assuming a handler is found, the program continues from the handler, not from where
the exception was thrown, or from the method that called the method in which theexception was thrown (unless that method had the handler) Once unwound, the stackframe is lost
Trang 12unwinds the stack, popping up through the calling methods until a handler is found.
If the runtime returns all the way throughMain( )without finding a handler, it nates the program Example 11-1 illustrates
termi-When you run this program in debug mode, an “Exception was unhandled” sage box comes up, as shown in Figure 11-1
mes-If you click View Detail, you find the details of the unhandled exception, as shown inFigure 11-2
This simple example writes to the console as it enters and exits each method.Main( )creates an instance of typeTestand callFunc1( ) After printing out theEnter Func1message,Func1( )immediately callsFunc2( ).Func2( )prints out the first message andthrows an object of typeSystem.Exception
Example 11-1 Throwing an exception
Trang 13Execution immediately shifts to handling the exceptions The CLR looks to seewhether there is a handler inFunc2( ) There is not, and so the runtime unwinds thestack (never printing theexitstatement) toFunc1( ) Again, there is no handler, andthe runtime unwinds the stack back toMain( ) With no exception handler there, thedefault handler is called, which opens the exception message box.
The catch Statement
In C#, an exception handler is called a catch block and is created with the catchkeyword
In Example 11-2, the throw statement is executed within a tryblock, and a catchblock is used to announce that the error has been handled
Figure 11-1 Unhandled exception
Figure 11-2 Exception details
Trang 14Example 11-2 Catching an exception
Console.WriteLine("Entering try block ");
throw new System.ApplicationException( );
Console.WriteLine("Exiting try block ");
}
catch
{
// simplified for this book; typically you would
// correct (or at least log) the problem
Console.WriteLine("Exception caught and handled.");
Entering try block
Exception caught and handled.
Trang 15Example 11-2 is identical to Example 11-1 except that now the program includes atry/catch block.
It is a common mistake to clutter your code withtry/catch blocks that don’t ally do anything and don’t solve the problem that the exception is pointing out It isgood programming practice to use atry/catchblock only where yourcatch has theopportunity to rectify the situation (with the exception of the topmost level where, at
actu-a minimum, you wactu-ant to factu-ail reactu-asonactu-ably gractu-acefully)
An exception to this practice is to catch and log the exception, and then rethrow itfor it to be handled at a higher level, or to catch the exception, add context informa-tion, and then nest that information bundled inside a new exception, as describedlater in this chapter
Catch statements can be generic, as shown in the previous example, or can be geted at specific exceptions, as shown later in this chapter
tar-Taking corrective action
One of the most important purposes of acatchstatement is to take corrective action.For example, if the user is trying to open a read-only file, you might invoke a methodthat allows the user to change the attributes of the file If the program has run out ofmemory, you might give the user an opportunity to close other applications If allelse fails, thecatchblock can log the error (or even send out email) so that you knowspecifically where in your program you are having the problem
Unwinding the call stack
Examine the output of Example 11-2 carefully You see the code enter Main( ),Func1( ), Func2( ), and thetryblock You never see it exit the tryblock, though itdoes exitFunc2( ),Func1( ), andMain( ) What happened?
When the exception is thrown, the normal code path is halted immediately and trol is handed to thecatchblock It never returns to the original code path It never
con-gets to the line that prints theexitstatement for thetryblock Thecatchblock dles the error, and then execution falls through to the code followingcatch
han-Withoutcatch, the call stack unwinds, but withcatch, it doesn’t unwind, as a result
of the exception The exception is now handled; there are no more problems, andthe program continues This becomes a bit clearer if you move thetry/catchblocks
up toFunc1( ), as shown in Example 11-3
Trang 16Example 11-3 Catch in a calling function
Trang 17This time the exception is not handled in Func2( ), it is handled in Func1( ) WhenFunc2( )is called, it prints theEnterstatement, and then throws an exception Execu-tion halts and the runtime looks for a handler, but there isn’t one The stackunwinds, and the runtime finds a handler inFunc1( ) Thecatchstatement is called,and execution resumes immediately following thecatchstatement, printing theExitstatement forFunc1( ) and then forMain( ).
Make sure you are comfortable with why the Exiting Try Block statement and theExit Func2statement aren’t printed This is a classic case where putting the code into
a debugger and then stepping through it can make things very clear
Try/Catch Best Practices
So far, you’ve been working only with genericcatchstatements Best practices, ever, dictate that you want, whenever possible, to create dedicatedcatchstatementsthat will handle only some exceptions and not others, based on the type of excep-tion thrown Example 11-4 illustrates how to specify which exception you’d like tohandle
how-Example 11-4 Specifying the exception to catch
// try to divide two numbers
// handle possible exceptions
public void TestFunc( )
Trang 18In this example, theDoDivide( )method doesn’t let you divide 0 by another number,nor does it let you divide a number by 0 It throws an instance ofDivideByZeroException if you try to divide by 0 If you try to divide 0 by anothernumber, there is no appropriate exception; dividing 0 by another number is a legalmathematical operation, and shouldn’t throw an exception at all For the sake of thisexample, assume you don’t want 0 to be divided by any number and throw anArithmeticException.
When the exception is thrown, the runtime examines each exception handler in
order and matches the first one it can When you run this witha=5andb=7, the put is:
out-5 / 7 = 0.71428out-571428out-57143
As you’d expect, no exception is thrown However, when you change the value ofa
to0, the output is:
} // end Test function
// do the division if legal
public double DoDivide(double a, double b)
{
if (b == 0)
throw new System.DivideByZeroException( );
if (a == 0)
throw new System.ArithmeticException( );
// throw new ApplicationException( );
Trang 19The exception is thrown, and the runtime examines the first exception,DivideByZeroException Because this doesn’t match, it goes on to the next handler,ArithmeticException, which does match.
In a final pass through, suppose you change a to 7 and b to 0 This throws theDivideByZeroException
You have to be particularly careful with the order of the catch
state-ments because the DivideByZeroException is derived from
ArithmeticException If you reverse the catch statements, the
DivideByZeroException matches the ArithmeticException handler, and
the exception won’t get to the DivideByZeroException handler In fact,
if their order is reversed, it’s impossible for any exception to reach the
DivideByZeroException handler The compiler recognizes that the
DivideByZeroException handler can’t be reached and reports a
com-pile error!
When catching the generic exception, it is often a good idea to at least log as muchabout the exception as possible by callingToStringon the exception To see this atwork, make three changes to the previous example:
• Change the declared value ofb from0 to2
• Uncomment the penultimate line of code
• Comment out the final line of code (as it will now be unreachable)
The output will look something like this:
Log this: System.SystemException: System error.
at SpecifyingCaughtException.Test.DoDivide(Double a, Double b) in C:\ \Specified
Exception
s\Program.cs:line 53
Notice that among other things, the generic exception tells you the file, the method,and the line number; this can save quite a bit of debugging time
The finally Statement
In some instances, throwing an exception and unwinding the stack can create aproblem For example, if you have opened a file or otherwise committed a resource,you might need an opportunity to close the file or flush the buffer
If there is some action you must take regardless of whether an exception is thrown(such as closing a file), you have two strategies to choose from One approach is toenclose the dangerous action in atryblock, and then to close the file in both thecatch and tryblocks However, this is an ugly duplication of code, and it’s error-prone C# provides a better alternative in thefinally block
Trang 20The code in thefinallyblock is guaranteed to be executed regardless of whether anexception is thrown TheTestFunc( )method in Example 11-5 simulates opening afile as its first action The method undertakes some mathematical operations, and thefile is closed It is possible that some time between opening and closing the file anexception will be thrown.
Keep the code in your finally block simple If an exception is thrown
from within your finally block, your finally block will not complete.
If this were to occur, it would be possible for the file to remain open The developerknows that no matter what happens, at the end of this method the file should beclosed, so the file close function call is moved to a finallyblock, where it will beexecuted regardless of whether an exception is thrown
Example 11-5 Using a finally block
// try to divide two numbers
// handle possible exceptions
public void TestFunc( )
Trang 21In this example, one of thecatch blocks is eliminated to save space, and afinallyblock is added Whether or not an exception is thrown, the finally block is exe-cuted (in both output examples you see the messageClose file here.).
You can create a finally block with or without catch blocks, but a
finally block requires a try block to execute It is an error to exit a
finally block with break, continue, return, or goto.
Exception Objects
So far, you’ve been using the exception as a sentinel—that is, the presence of theexception signals the error—but you haven’t touched or examined the Exceptionobject itself TheSystem.Exceptionobject provides a number of useful methods andproperties TheMessageproperty provides information about the exception, such as
// do the division if legal
public double DoDivide(double a, double b)
This line may or may not print
Close file here.
Example 11-5 Using a finally block (continued)
Trang 22why it was thrown TheMessageproperty is read-only; the code throwing the tion can set theMessage property as an argument to the exception constructor.TheHelpLinkproperty provides a link to the help file associated with the exception.This property is read/write.
excep-VB 6 programmers take note: in C#, you need to be careful when
declaring and instantiating object variables on the same line of code If
there is a possibility that an error could be thrown in the constructor
method, you might be tempted to put the variable declaration and
instantiation inside the try block But, if you do that, the variable will
only be scoped within the try block, and it can’t be referenced within
the catch or finallyblocks The best approach is to declare the object
variable before thetry block and instantiate it within the try block.
TheStackTraceproperty is read-only and is set by the runtime In Example 11-6, theException.HelpLinkproperty is set and retrieved to provide information to the userabout theDivideByZeroException TheStackTraceproperty of the exception can pro-
vide a stack trace for the error statement A stack trace displays the call stack: the
series of method calls that lead to the method in which the exception was thrown
Example 11-6 Working with an exception object
// try to divide two numbers
// handle possible exceptions
public void TestFunc( )
Trang 23// most derived exception type first catch (System.DivideByZeroException e) {
Console.WriteLine(
"DivideByZeroException!" + e); }
// do the division if legal
public double DoDivide(double a, double b) {
Open file here
DivideByZeroException! Msg: Attempted to divide by zero HelpLink: http://www.libertyassociates.com
Here's a stack trace:
at ExceptionObject.Test.DoDivide(Double a, Double b)
in c:\ exception06.cs:line 56
at ExceptionObject.Test.TestFunc( )
in exception06.cs:line 22
Close file here.
Example 11-6 Working with an exception object (continued)
Trang 24In the output, the stack trace lists the methods in the reverse order in which theywere called; that is, it shows that the error occurred inDoDivide( ), which was called
byTestFunc( ) When methods are deeply nested, the stack trace can help you stand the order of method calls
under-In this example, rather than simply throwing aDivideByZeroException, you create anew instance of the exception:
DivideByZeroException e = new DivideByZeroException( );
You don’t pass in a custom message, and so the default message will be printed:
DivideByZeroException! Msg: Attempted to divide by zero.
You can modify this line of code to pass in a default message:
new DivideByZeroException(
"You tried to divide by zero, which is not meaningful");
In this case, the output message will reflect the custom message:
DivideByZeroException! Msg:
You tried to divide by zero, which is not meaningful
Before throwing the exception, set theHelpLink property:
Trang 25Chapter 12
CHAPTER 12
When a head of state dies, the president of the United States typically doesn’t havetime to attend the funeral personally Instead, he dispatches a delegate Often, thisdelegate is the vice president, but sometimes the VP is unavailable, and the presidentmust send someone else, such as the secretary of state or even the first lady Hedoesn’t want to “hardwire” his delegated authority to a single person; he might dele-gate this responsibility to anyone who is able to execute the correct internationalprotocol
The president defines in advance what responsibility will be delegated (attend thefuneral), what parameters will be passed (condolences, kind words), and what value
he hopes to get back (good will) He then assigns a particular person to that gated responsibility at “runtime” as the course of his presidency progresses
dele-Events
In programming, you are often faced with situations where you need to execute aparticular action, but you don’t know in advance which method, or even whichobject, you’ll want to call upon to execute it The classic example of this is themethod called to handle a button press, a menu selection, or some other “event.”
An event, in event-driven programming (like Windows!), is when something
hap-pens—often as a result of user action, but at times as a result of a change in systemstate or a result of a message begin received from outside the system (e.g., via theInternet)
You must imagine that the person who creates a button (or listbox or other control)
will not necessarily be the programmer who uses the control The control inventor
knows that when the button is clicked, the programmer using the button will wantsomething to happen, but the inventor can’t know what!