How to code .NET - Pdf 39

CYAN
MAGENTA
YELLOW
BLACK
PANTONE 123 CV
this print for content only—size & color not accurate
7" x 9-1/4" / CASEBOUND / MALLOY
(0.625 INCH BULK -- 232 pages -- 60# Thor)
THE EXPERT’S VOICE
®
IN .NET
Christian Gross
How to
Code .NET
Tips and Tricks for Coding .NET 1.1
and .NET 2.0 Applications Effectively
BOOKS FOR PROFESSIONALS BY PROFESSIONALS
®
How to Code .NET: Tips and Tricks for Coding
.NET 1.1 and .NET 2.0 Applications Effectively
Dear Reader,
Like you, I am a coder, architect, and developer. People who are coders, archi-
tects, or developers strive to do their best, and if given the choice they will
always do something correctly. Of course, this begs the question: Why do we
have so many bugs in our code?
I think the main reason for buggy code is that we are all short on time. We
don’t have the luxury of investigating new Framework features fully or exploring
innovative new techniques as thoroughly as we would like, because we’re all
watching the clock. That means our code has bugs—the new Framework feature
we implemented doesn’t work quite as expected, and the new best practice we
put in place doesn’t seem to work the same way for every input. These bugs are

89253 59744
6
Companion
eBook
Available
Companion eBook
See last page for details
on $10 eBook version
www.apress.com
SOURCE CODE ONLINE
forums.apress.com
FOR PROFESSIONALS BY PROFESSIONALS

Join online discussions:
Christian Gross
How to Code .NET
Tips and Tricks for Coding
.NET 1.1 and .NET 2.0
Applications Effectively
7443FM.qxd 9/21/06 10:46 PM Page i
How to Code .NET: Tips and Tricks for Coding .NET 1.1 and .NET 2.0 Applications Effectively
Copyright © 2006 by Christian Gross
All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means,
electronic or mechanical, including photocopying, recording, or by any information storage or retrieval
system, without the prior written permission of the copyright owner and the publisher.
ISBN-13 (pbk): 978-1-59059-744-6
ISBN-10 (pbk): 1-59059-744-3
Printed and bound in the United States of America 9 8 7 6 5 4 3 2 1
Trademarked names may appear in this book. Rather than use a trademark symbol with every occurrence
of a trademarked name, we use the names only in an editorial fashion and to the benefit of the trademark

About the Author
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
About the Technical Reviewer
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
Acknowledgments
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
Introduction
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii

CHAPTER 1
Testing Your Code
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

CHAPTER 2
.NET Runtime- and Framework-Related Solutions
. . . . . . . 31

CHAPTER 3
Text-Related Solutions
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

CHAPTER 4
C# Coding Solutions
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

INDEX
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
iii
7443FM.qxd 9/21/06 10:46 PM Page iii
7443FM.qxd 9/21/06 10:46 PM Page iv

Keeping Value Types and Reference Types Straight
. . . . . . . . . . . . . . . . . . 31
Using Delegates
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Versioning Assemblies
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Loading and Unloading Assemblies Dynamically
. . . . . . . . . . . . . . . . . . . . . 47
Loading Assemblies Dynamically
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Loading and Unloading Assemblies Dynamically
. . . . . . . . . . . . . . . . 52
Implementing GetHashCode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Thinking of .NET Generics as Black Boxes
. . . . . . . . . . . . . . . . . . . . . . . . . . 72
Figuring Out What Generic Methods Do
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Using the new and class Keywords with .NET Generics
. . . . . . . . . . . . . . . 82

CHAPTER 3
Text-Related Solutions
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Converting a String to an Array and Vice Versa
. . . . . . . . . . . . . . . . . . . . . . 85
Parsing Numbers from Buffers
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Processing Plain-Vanilla Numbers in Different Cultures
. . . . . . . . . . 89

Namespaces
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
Class and Interface Identifiers
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
Understanding the Overloaded Return Type and Property
. . . . . . . . . . . . 139
Nullable Types: A Null Is Not Always a Null
. . . . . . . . . . . . . . . . . . . . . . . . . 145
Abstract-Class Bridge-Pattern Variation
. . . . . . . . . . . . . . . . . . . . . . . . . . . 148
Nested Private-Class Bridge-Pattern Variation
. . . . . . . . . . . . . . . . . . . . . . 151
Dealing with Marker Interfaces or Base Classes
. . . . . . . . . . . . . . . . . . . . 153
Editing Text Using the Command Pattern
. . . . . . . . . . . . . . . . . . . . . . 154
Marker Interfaces and Their Dependencies
. . . . . . . . . . . . . . . . . . . 156
How Marker Interfaces Dependencies Are Implemented
. . . . . . . . 157
A Null Value Is Not Always a Null State
. . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
The Essentials of the Factory Pattern
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
The Classical Factory Pattern
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170
More Sophisticated Factory Implementations
. . . . . . . . . . . . . . . . . 173
Don’t Expose a Class’s Internal State
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178

ever since I learned how to peek and poke my first bytes. I have written various books,
including Ajax and REST Recipes: A Problem-Solution Approach, Foundations of Object-
Oriented Programming Using .NET 2.0 Patterns, and A Programmer’s Introduction to
Windows DNA, all available from Apress.
These days I enjoy coding and experimenting with .NET, as it is a fascinating environ-
ment. .NET makes me feel like a kid opening a present on Christmas morning. You had an idea
what the gift was, but you were not completely sure. And with .NET there is no relative giving
you socks or a sweater. It’s excitement all the way!
About the Author
7443FM.qxd 9/21/06 10:46 PM Page vii
7443FM.qxd 9/21/06 10:46 PM Page viii
About the Technical Reviewer

JASON LEFEBVRE is vice president and founding partner of Intensity Software, Inc.
(), which specializes in providing custom Microsoft .NET
applications, IT consulting services, legacy system migration, and boxed software products to
a rapidly growing set of clients. Jason has been using Microsoft .NET since its Alpha stages in
early 2000 and uses Visual Studio and the Microsoft .NET Framework daily while creating
solutions for Intensity Software’s clients. Jason has been a participating author for a number of
books and has written numerous articles about Microsoft .NET-related topics.
ix
7443FM.qxd 9/21/06 10:46 PM Page ix
7443FM.qxd 9/21/06 10:46 PM Page x
Acknowledgments
T
his book would not be complete without you, the reader. I came upon the idea for this
book after I realized that I had a number of “canned” solutions to problems that readers of my
articles, clients, or attendees of my conference sessions posed to me. For example, Andreas
Penzold, a reader of my materials, worked with me to figure out what you can expect of
GetHashCode and Equals.

The source code is available in the Source Code/Download section of the Apress website
(). Additionally, you can visit />to download the code.
xiii
7443FM.qxd 9/21/06 10:46 PM Page xiii
7443FM.qxd 9/21/06 10:46 PM Page xiv
Testing Your Code
T
his book will introduce a series of techniques for how to write code in .NET. The best way to
learn is to run a series of tests, and thus most of the examples in this book use a utility called
NUnit
1
because I want to implement test-driven development (TDD). In a nutshell the idea
behind TDD is to architect, develop, and test your code at the same time. Additionally, it helps
me write pieces of code that you can verify. In general development, the benefit of testing and
developing code at the same time is that when your code is finished you know that it is tested
enough to make you reasonably sure that stupid errors will not happen. The quality assurance
department at your company will not complain about the lack of quality.
This chapter will take you through the steps for writing your own test routines. You’ll learn
about publicly available testing tools and their uses. Then you’ll move on to topics such as
how to define tests, why contexts are important, and how to implement mock objects.
Quick Notes About TDD
When implementing TDD, purists advise a top-down development approach in which the test
is written before the code. However, due to the nature of the development environments that
use IntelliSense, a developer will tend to write the code first, and then the test. When using
IntelliSense, code is developed using a bottom-up approach, thus not adhering to the TDD
purists’ approach. It does not matter which approach you take.
What matters is the cycle: write some code and some tests or vice versa, and then run the
tests. To keep TDD effective, those cycles might be a few minutes, a half hour, or (for a complex
algorithm) a couple of hours. Your TDD cycle is not time-dependent, but feature-dependent.
When I speak of features I don’t mean high-level “create invoice”–type features. I mean fea-

If you use TDD when you write your test, write your code, and test your code, the code is
fresh in your mind. You are mentally alert and if something returns true when it should have
returned false, and you can fix it easily because the number of permutations and combina-
tions is small enough to figure out in your head. Having tested and implemented one
functionality, your next step is to implement some other functionality. If the second function-
ality uses the already implemented functionality, then you don’t need to waste brain cycles on
guessing whether the implemented functionality works. You know the existing functionality
works because it is already tested. That doesn’t mean you ignore the already implemented
functionality, because bugs can still pop up. What it means is that you do not need to care as
much as you would otherwise, and you can concentrate on other issues.
Getting Started with TDD and NUnit
NUnit is both a framework and application; it is a framework that you use to write tests, and
it is an application to run the tests that you wrote. NUnit is simple, and there is very little
imposed overhead. What NUnit does not do is write the test for you. Thus the effectiveness of
NUnit depends on you. If you write complete tests, then NUnit provides complete test results.
Metaphorically speaking, NUnit is your hammer, and how you use the hammer is your
responsibility. If you use your hammer to dig holes in the ground, then you are not going to
be effective. However, if you know how to swing the hammer and position nails, then your
hammer can be extremely effective.
Assuming you decided to download the NUnit distribution from />the expanded downloaded distribution file contains a number of subdirectories: bin, doc, and
samples. The purpose of each subdirectory is explained here:
• bin contains the assemblies and applications that are needed to use NUnit.
• doc contains HTML files that provide the documentation on how to use NUnit as both
an application and a framework. This book covers much of the information in the
HTML files; refer to the NUnit documentation for specific details.
CHAPTER 1

TESTING YOUR CODE2
7443CH01.qxd 9/12/06 10:15 PM Page 2
• samples contains a number of NUnit samples in different languages (VB.NET, C#, and

Visual Studio .NET offers similar functionality. Though to use NUnit you have to install
TestDriven.NET.
3
When TestDriven.NET is installed, you have similar capabilities to the ones
X-develop provides, but a different look and feel. Figure 1-2 illustrates how to test the exact
same source code as shown in Figure 1-1 using TestDriven.NET and Visual Studio.
Figure 1-2. Executing NUnit tests in Visual Studio
To run a test in Visual Studio using TestDriven.NET, you move your mouse over the attrib-
ute TestFixture, or anywhere in a class that has the attribute TestFixture. Once you have
moved the mouse to the proper location, do a context-sensitive click. From the menu that
appears, click the item Run Test(s) to run the tests; the test results are generated in the Output
window of Visual Studio.
Regardless of whether you use X-develop, Visual Studio .NET, or your own personal devel-
opment environment, it is not necessary to run the tests in the context of a development
environment; you can use the nunit-gui.exe and nunit-console.exe programs, as mentioned
earlier. I’ve illustrated the IDEs here because you’ll have a simpler time coding, compiling, and
testing when you don’t have to leave the comfort of the IDE.
CHAPTER 1

TESTING YOUR CODE4
3.
/>7443CH01.qxd 9/12/06 10:15 PM Page 4
One last item to note regarding writing tests and the location of the tests: If you look closely
at Figures 1-1 and 1-2 you will see that the test code and the code to be tested are in the same
file. When writing production tests, that is not the case. In a production setting the code to be
tested and the tests are in two separate assemblies. You want separate assemblies because test
code is debug code and you don’t want to ship a final product that contains debug code. How-
ever, there is one notable exception: types declared with the internal attribute need to have the
tests embedded in the assembly. When you have to combine tests and code in a single assembly,
take advantage of conditional compilation, allowing debug builds to contain the tests and

called.
CHAPTER 1

TESTING YOUR CODE 5
7443CH01.qxd 9/12/06 10:15 PM Page 5
Writing Tests Using Contexts and Results
When writing a test the objectives are to establish a context and verify the results that the
context generates. Writing a test is not just about instantiating a type, calling a method, and
ensuring that everything worked. If you think a test is about types with methods and the
parameters necessary to call those methods, then you are missing the point of writing tests.
When you write a large number of tests without considering the context, you are using brute
force to verify the correctness of an implementation. Brute-force verification will test func-
tionality and allow you to check-mark your code as being tested, but the question is, which
functionality has been tested? Metaphorically speaking, brute-force testing is like making a
baseball glove and testing how well the glove can catch a beach ball, a baseball, and a soccer
ball. It might be able to catch all those ball types, but the test is pointless—it is a baseball
glove, after all. Always consider the context.
Let’s examine what the code in our applications represents. When we write code in our
minds, the code is meant to execute in a certain context. The context could be to instantiate
an invoice, create a sales total for the month, or what have you. Each context tends to be
business-related. The disjoint between code and context happens in the creation of the lower-
level classes. We become preoccupied with how classes instantiate other classes. When we try
to figure out all of the permutations and combinations of how to instantiate a class, we
become wrapped up in technical details. And with more classes we become more detailed
about the technical ramifications and problems. We have lost sight of the actual problem; we
are writing code that forces baseball gloves to catch beach balls. The same is true when the
tests are written for the lower-level classes.
So why do we care so much about technical details? Because we are striving for complete-
ness, but completeness involves too many permutations and combinations. What we should
strive for is complete code for the context. If a context that we did not account for is created,

being unable to access the test class and method. A test class may have multiple tests and can
be embedded in a namespace. What you name the class or method does not matter so long as
the name is prefixed with the appropriate .NET attribute.
When you’re wondering what test to write, write the test for the first thing that comes to
mind. With Mathematics that would be the addition of two numbers, and the first test would
be written as follows:
Source: /Volume01/LibVolume01/GettingStartedWithNUnit.cs
[TestFixture]
public class TestMath {
[Test]
public void TestAdd() {
Mathematics obj = new Mathematics();
Assert.AreEqual( 6, obj.Add( 2, 4), "Addition of simple numbers");
}
}
In the test method TestAdd, the class Mathematics is instantiated and then the method Add
is called. How you instantiate and call the code is important and must resemble the code you
would write in an application. Again using the baseball glove metaphor, creating a test situa-
tion where a baseball is fired at 145 kilometers per hour at a glove is a good test of stressing a
glove, but it’s not realistic. Most likely a ball of that speed would be thrown by a pitcher, and
hence the appropriate glove is a catcher’s mitt.
The preceding test code is an example of using Mathematics in an application scenario,
which is a context. The return value from the method Add is passed directly to the method
Assert.AreEqual. The method Assert.AreEqual is used to verify that the result Add returns is
correct.
The method Assert.AreEqual, and in particular the Assert class, play a very important
role when writing tests. A test is a context of the code to be tested. When the code is being exe-
cuted, there is no difference between the test bed and the production environment. The test
bed needs to verify what was executed and does this using the verification class Assert. The
production environment uses the same code and assumes what was executed produced a cor-

Mathematics obj = new Mathematics();
Assert.AreEqual( 6, obj.Add( 2, 4), "Addition of simple numbers");
}
[Test]
public void TestAddLargeNUmbers() {
Mathematics obj = new Mathematics();
Assert.AreEqual( 4000000000, obj.Add( 2000000000, 2000000000),
"Addition of large numbers");
}
}
The added test is TestAddLargeNumbers, and again the method Assert.AreEqual is used.
The numbers 2,000,000,000 and 2,000,000,000 are added together and tested to see if the
return value is 4,000,000,000. We know that 4,000,000 will not be the result, but for illustration
purposes we write the test as if it would work. Take a moment to look at the code again, and
then look at the output. Notice something? The oddity is that the code could be executed,
meaning that the code can be compiled without any errors. If you ignore the oddity for now
and run the test, you’ll get the following result:
NUnit.Framework.AssertionException: Addition of large numbers
expected: <4000000000>
but was: <-294967296>
at NUnit.Framework.Assert.AreEqual(Decimal expected,

Decimal actual,

String message, Object[] args)
at NUnit.Framework.Assert.AreEqual(Decimal expected,

Decimal actual, String message)
CHAPTER 1


program will tell you so. When you have a failing context your code must beep very loudly to
tell the caller that something went wrong. Not testing failing contexts can have dire conse-
quences. Imagine being Bill Gates and depositing $2 billion in an account that has $2 billion.
Using the Add method that we currently have, it would seem that Bill Gates owes the bank $29
million. Bill Gates would not be a happy camper, whereas the bank might be thrilled.
NUnit has the ability to test failing contexts using the attribute ExpectedException. The
attribute ExpectedException expects that for a failing context the tested code will generate an
exception. The current implementation Add adds two numbers and generates an overflow,
but no exceptions are raised. Had the current implementation of Add been tested with the
ExpectedException attribute, the test would have failed because no exception was generated.
An exception can be generated for an overflow situation by using the checked keyword in the
implementation of Add. The rewritten code and associated test code is as follows (with modified
pieces appearing in boldface):
class Mathematics {
public int Add( int param1, int param2) {
checked {
return param1 + param2;
}
}
CHAPTER 1

TESTING YOUR CODE 9
7443CH01.qxd 9/12/06 10:15 PM Page 9
}
[TestFixture]
public class TestMath {
[Test]
public void TestAdd() {
Mathematics obj = new Mathematics();
Assert.AreEqual( 6, obj.Add( 2, 4), "Addition of simple numbers");

• The difficult part of defining a context is being specific enough; a general context will
miss tests. It is difficult to find the various contexts because that requires a good under-
standing of the code you are testing.
• If two contexts generate identical results, then you may have a bug. The problem is that
two contexts identify two different operating conditions, and generating the same
result may indicate missing functionality or improperly implemented functionality.
CHAPTER 1

TESTING YOUR CODE10
7443CH01.qxd 9/12/06 10:15 PM Page 10


Nhờ tải bản gốc
Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status