You are on page 1of 232

CYAN YELLOW

MAGENTA BLACK
PANTONE 123 CV

BOOKS FOR PROFESSIONALS BY PROFESSIONALS ® THE EXPERT’S VOICE ® IN .NET


Companion
Author of
eBook
Ajax Patterns How to Code .NET: Tips and Tricks for Coding Available
and Best Practices
Ajax and REST Recipes: A
.NET 1.1 and .NET 2.0 Applications Effectively

How to Code .NET


Problem-Solution Approach
Dear Reader,
Foundations of Object-
Oriented Programming Like you, I am a coder, architect, and developer. People who are coders, archi-

How to
Using .NET 2.0 Patterns 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

Code .NET
frustrating and can often be very difficult to solve.
This book is a response to that problem. In it I have investigated and recorded
my experiences of a wide range of .NET Framework features. They’re arranged
in simple, bite-sized sections dedicated to problem solving, informing you of
little-known functionality and keeping you up to date with the latest design
thinking. It’s a road map to your more effective use of the .NET Framework.
For example, the .NET Framework 2.0 introduced the yield keyword. On the
face of it, this is a really cool new piece of functionality that we’d all like to use.
But what’s it really like? Is it buggy? Is it going to be the future of all iterators?
This book digs into these questions and more to provide you with the answers
that you need.

Christian Gross
Tips and Tricks for Coding .NET 1.1
and .NET 2.0 Applications Effectively

Join online discussions:

forums.apress.com
SOURCE CODE ONLINE
Companion eBook FOR PROFESSIONALS BY PROFESSIONALS ™

www.apress.com

See last page for details


on $10 eBook version ISBN 1-59059-744-3
90000 Christian Gross
Gross
Shelve in
.NET
6 89253 59744 6 9 781590 597446
User level:
Beginner–Intermediate

this print for content only—size & color not accurate 7" x 9-1/4" / CASEBOUND / MALLOY
7443CH01.qxd 9/12/06 10:15 PM Page 1

CHAPTER 1

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
NUnit1 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-
tures that are lower-level, like “initialized array.” The key to TDD is to implement and test a
small piece of functionality before moving on to the next functionality. When writing code
using TDD, you will pick a feature to implement, which then allows another feature to be
implemented, and so on. Eventually the application will be completely implemented with
associated tests.
If you develop your code using TDD, you will experience the following benefits:

• Code and tests that are written simultaneously expose integration, data, and logic
problems early.

• Code is broken down into smaller chunks, making it easier to maintain an overview of
the contained logic.

1. http://www.nunit.org/ 1
7443CH02.qxd 9/14/06 11:12 PM Page 31

CHAPTER 2

.NET Runtime- and Framework-


Related Solutions

T he solutions covered in this chapter relate to the .NET runtime and are not particular to any
specific language. Of course, it is not advisable to present the bulk of the solutions using
Microsoft Intermediate Language (MSIL), and a language must be used. For the scope of this
chapter and book, I’ve chosen C# as the language.

Keeping Value Types and Reference Types Straight


The .NET documentation states that a class is a reference type, and a struct is a value type.
Generally speaking, the difference between the two seems irrelevant from a coding perspec-
tive. Both a value type and a reference type use memory space. One of the features that
differentiates them is where each type stores its memory space. An instantiated reference type
is stored on the heap, and an instantiated value type is stored on the stack. Sounds good,
right? Next question: what are a heap and a stack? The heap is like a global variable in that all
objects stored on the heap are accessible by all other objects. However, it does not behave as
a global variable because .NET manages the access of the references. The stack is a memory
space that is private between the caller and callee. In Figure 2-1 a method call uses a stack that
contains a value and a reference type.
In Figure 2-1 two types are declared: MyStruct is declared as a struct and hence a value
type, and MyObject is declared as a class and hence reference type. The method
Example.Method has two parameters, where the first is a value type and the second is a refer-
ence type. When the method is called, the calling stack contains the two parameters, but how
they are stored in the stack is different. In the case of the structure the complete state of the
value type is stored on the stack. If the structure required 10 bytes of memory, then the stack
would make room for the 10 bytes of memory. For the reference type, the state is stored in the
heap, and a reference to the memory on the heap in stored on the stack.
Let’s look at how each type can be manipulated in a function call. For example, one rami-
fication of using a value type is that any change in the stack value will not travel from callee to
caller. Consider the following source code, which is an implementation of a function that has a
number of modified value parameters; what interests us is to know which parameters are
modified.

31
7443CH03.qxd 9/21/06 4:36 PM Page 85

CHAPTER 3

Text-Related Solutions

T here are many countries, cultures, and languages on this planet we call home. People in
each country may speak a single language or multiple languages, and each country may host
cultures. In the early days of computing, you could choose any language to use—so long as it
was American English. As time progressed we became able to use software in multiple lan-
guages and for multiple cultures. .NET raises the bar; it lets you mix and match cultures,
languages, and countries in a single compiled application. This chapter is about processing
text in a multiculture/multicountry/multilanguage situation.

Converting a String to an Array and Vice Versa


In previous programming languages like C and C++, strings were buffer arrays, and managing
strings was fraught with complications. Now that .NET strings are their own types, there is still
grumbling because managing a string as bits and bytes has become complicated.
To manage a string as bits and bytes or as an array, you need to use byte arrays, which are
commonly used when reading and writing to a file or network stream. Let’s look at a very sim-
ple example that reads and writes a string to a byte array and vice versa.

Source: /Volume01/LibVolume01/StringToBufViceVersa.cs

[Test]
public void SimpleAsciiConversion() {
String initialString = "My Text";
byte[] myArray =
System.Text.Encoding.ASCII.GetBytes(
initialString);
String myString =
System.Text.Encoding.ASCII.GetString(
myArray);
Assert.AreEqual( initialString, myString);
}

In the example the string initialString contains the text "My Text". Using the predefined
instance System.Text.Encoding.ASCII and method GetBytes, the string is converted into a
byte array. The byte array myArray will contain seven elements (77, 121, 32, 84, 101, 120, 116)
that represent the string. The individual numbers correspond to representations of the letter
from the ASCII1 table. Byte arrays are examples of lookup tables, where the value of a byte

1. http://www.lookuptables.com/ 85
7443CH04.qxd 9/17/06 1:37 PM Page 117

CHAPTER 4

C# Coding Solutions

I n the previous chapters, the focus was on how to use the .NET API. All of the examples were
illustrated using C#, but the examples did not use any particular feature of C#. The examples
could have been implemented with VB.NET or any other .NET language. That changes in this
chapter, as the focus is on the C# programming language. Specific features of the language will
be dissected and analyzed. Sometimes patterns will be used, and other times not. In the over-
all scheme of this chapter, the idea is to give you a better understanding of what C# is capable
of and not capable of.

What Does the Yield Keyword Really Generate?


The yield keyword was added to C# 2.0 and is used to simplify the implementation of enu-
meration in custom classes. Before the yield keyword, we had to implement a number of
interfaces to have a class support enumeration. Implementation of enumeration was a pain,
yet we did it so that we could take advantage of the foreach looping mechanism. The foreach
looping mechanism makes it easy to iterate a collection.
The yield keyword simplifies the implementation of iterable collections, but it also allows
us to move beyond collections and into result sets. Using the yield keyword, we can convert
calculated sequences into collections. Let me give an example. Let’s say that I am calculating
the sequence of square roots for all numbers. Saying that you will calculate a sequence of
numbers for all numbers should already indicate to you that a giant array would be calculated,
as numbers are infinite.
Assuming for the moment that we do create an infinite array, let’s look at how those num-
bers would be generated without using the yield keyword. There would be a piece of code
that would call the algorithm to generate the sequence of numbers. The sequence of numbers
would be added to an array, which is returned to the calling code when the algorithm has
completed. Yet we are calculating an infinite sequence of numbers, meaning that the algo-
rithm will never end and the array will never be complete.
Of course, in reality, algorithms do end, and arrays do become complete. But the example
illustrates that if you were to generate a collection that could be iterated, you must first gener-
ate the collection and then iterate the collection. This would mean you first allocate the space
for an array and then fill the array, resulting in a not-as-efficient solution. The yield keyword
is more efficient, because it allows a calculation to generate numbers on the fly, making it
appear like there is a collection of precalculated numbers.

117

You might also like