This article discusses:
|
This article uses the following technologies:
.NET, C# or Visual Basic .NET, Visual Studio .NET |
Contents
You cannot expect to build a
first-class application unless you use the best available tools. Besides
well-known tools such as Visual Studio® .NET, there are a multitude of
small, lesser-known tools available from the .NET community. In this
article, I'm going to introduce you to some of the best free tools
available today that target .NET development. I'll walk you through a
quick tutorial of how to use each of them, some of which will save you a
minute here and there, while others may completely change the way that
you write code. Because I am squeezing so many different tools into this
single article, I will not be able to cover each of them extensively,
but you should learn enough about each to decide which tools are useful
for your projects.
Snippet
Compiler
The Snippet Compiler is a
small Windows®-based application that allows you to write, compile, and
run code. This tool is useful if you have small pieces of code for which
you don't want to create an entire Visual Studio .NET project (along
with all the files that come with it).
As an example, let's say that I wanted to show
you how to launch another application from the Microsoft® .NET
Framework. In the Snippet Compiler I would start by creating a new file
which creates a small console application. The snippet can be created
inside the Main method of the console application, which is what I will
do here. The following code snippet demonstrates how to create an
instance of Notepad from the .NET Framework:
Of course this snippet would not compile by itself, but that is
where Snippet Compiler comes into play. Figure 1
shows
this code sample in Snippet Compiler.
System.Diagnostics.Process proc = new System.Diagnostics.Process(); proc.StartInfo.FileName= "notepad.exe"; proc.Start(); proc.WaitForExit();
Figure 1 Snippet Compiler
To test this snippet, just press the play
button (green triangle), and it will run in debug mode. The snippet will
generate a console application popup, and Notepad will appear. When you
close Notepad, the console application will close as well.
Personally, I have found Snippet Compiler to
be invaluable when trying to create a small example for someone who has
asked me for help, when normally I would have to create a project, make
sure everything compiles, send them the code snippet, and then delete
the project. Snippet Compiler makes this process much easier and much
more pleasant.
Snippet Compiler was
written by Jeff Key and can be downloaded from http://www.sliver.com/dotnet/SnippetCompiler
.
Regulator
Regulator is the most recent addition to my
top tools list. It is a full-featured tool that makes it easy to build
and test regular expressions. There is a renewed interest in regular
expressions because of the excellent support for them in the .NET
Framework. Regular expressions are used to define patterns in strings
based on characters, frequency, and character order. They are most
commonly used as a means to validate user input or as a way to find a
string of characters inside a larger string—for instance, when looking
for a URL or e-mail address on a Web page.
Regulator allows you to enter a regular
expression and some input against which you would be running this
expression. This way you can see how the regular expression will act and
what kind of matches it will return before implementing it in your
application. Figure 2
shows Regulator with a simple
regular expression.
Figure 2 Regulator
The document contains the
regular expression, in this example it is [0-9]* which should match any
number of digits in a row. The box in the bottom-right contains the
input for this regular expression, and the box on the bottom-left shows
the matches that this regular expression finds in the input. The ability
to write and test regular expressions in a separate application like
this is much easier than trying to work with them in your app.
One of the best features in Regulator is the
ability to search the online regular expressions library at
regexlib.com. For example, if you enter the string "phone" in the search
box, you will find more than 20 different regular expressions that will
match various phone numbers, including expressions for UK, Australian,
and many other phone numbers.
Regulator
was written by Roy Osherove and can be downloaded at http://osherove.com/tools
.
CodeSmith
CodeSmith is a template-based code-generation
tool that uses a syntax similar to ASP.NET to generate any type of code
or text. Unlike many other code-generation tools, CodeSmith does not
require you to subscribe to a particular application design or
architecture. Using CodeSmith, you can generate anything from a simple,
strongly typed collection to an entire application.
When you are building an application, you will
often find yourself repeating certain tasks, whether it's writing data
access code or building custom collections. CodeSmith is particularly
useful at such times because you can write templates to automate those
tasks and not only improve your productivity but also automate the tasks
that are the most tedious to perform.
CodeSmith ships with a number of templates,
including ones for all the .NET collection types as well as ones to
generate stored procedures, but the real power of this tool comes from
being able to create custom templates. To get you started, I'll provide a
quick introduction to building a custom template.
Building a Custom Template
CodeSmith templates are simply text files
which you can create in any text editor. Their only requirement is that
they be saved with the .cst file extension. The sample template that I'm
going to build will accept a string and then build a class based on
that string. The first step to creating a template is to add the
template header, which declares the language of the template, the target
language, and a brief description of the template:
<%@ CodeTemplate Language="C#" TargetLanguage="C#" Description="Car Template" %>
The next part of the
template is the property declarations, where you declare the properties
that will be specified each time the template is run. With this
template, the single property that I'm going to use is just a string, so
the property declaration looks like this:
<%@ Property Name="ClassName" Type="String" Category="Context" Description="Class Name" %>
[Editor's
Update - 6/16/2004:
The code in Figure 3 has been updated to be
safe for multithreaded operations.]
Figure 3 Class Generation
Custom Template
public sealed class <%= ClassName %> { private static volatile <%= ClassName %> _instance; private <%= ClassName %>() {} private static readonly object _syncRoot = new object(); public static <%= ClassName %> Value { get { if (_instance == null) { lock(_syncRoot) { if (_instance == null) { _instance = new <%= ClassName %>(); } } } return _instance; } } }
public sealed class SingletonClass { private static volatile SingletonClass _instance; private SingletonClass() {} private static readonly object _syncRoot = new object(); public static SingletonClass Value { get { if (_instance == null) { lock(_syncRoot) { if (_instance == null) { _instance = new SingletonClass(); } } } return _instance; } } }
As you can see, the template
will take the string input and generate a singleton class using that
class name. In the template body, the same opening and closing tags are
used as in ASP.NET. In this template, I am simply inserting the property
value, but you can also use any type of .NET code inside these tags.
Once the template is complete, you load it into CodeSmith by either
double-clicking or opening it from the CodeSmith application. Figure
4
shows this template loaded into CodeSmith.
Figure 4 CodeSmith Template
You can see that the property
on the left is the one I declared in the template. If I enter
"SingletonClass" as the class name and click the Generate button, the
class shown in the bottom part of Figure 3
will be
generated.
CodeSmith is relatively
easy to use and can produce some incredible results if applied
correctly. One of the most common sections of an application that is
targeted for code generation is the data access layer. CodeSmith
includes a special assembly called the SchemaExplorer which can be used
to generate templates from tables, stored procedures, or almost any
other SQL Server™ object.
CodeSmith
was written by Eric J. Smith and is available for download at http://www.ericjsmith.net/codesmith
.
NUnit
NUnit is an open source unit testing framework
built for the .NET Framework. NUnit allows you to write tests in the
language of your choice to test a specific function of your application.
Unit tests are an excellent way to test the functionality of your code
when you first write it, and also to provide a method for regression
testing of your application. The NUnit application provides a framework
for writing unit tests, as well as a graphical interface to run these
tests and view the results.
Writing an NUnit Test
As an example, I'm going to test the
functionality of the Hashtable class in the .NET Framework to determine
if two objects can be added and then retrieved. My first step will be to
add a reference to the NUnit.Framework assembly, which will give me
access to the attributes and methods of the NUnit framework. Next I'll
create a class and mark it with the TestFixture attribute. This
attribute lets NUnit know that this class contains NUnit tests:
using System; using System.Collections; using NUnit.Framework; namespace NUnitExample { [TestFixture] public class HashtableTest { public HashtableTest() { } } }
Next I'll create a method
and mark it with the [Test] attribute so that NUnit knows that this
method is a test. Then I'll set up a Hashtable and add two values to it,
then use the Assert.AreEqual method to see if I can retrieve the same
values that I added to the Hashtable, as shown in the following:
[Test] public void HashtableAddTest() { Hashtable ht = new Hashtable(); ht.Add("Key1", "Value1"); ht.Add("Key2", "Value2"); Assert.AreEqual("Value1", ht["Key1"], "Wrong object returned!"); Assert.AreEqual("Value2", ht["Key2"], "Wrong object returned!"); }
This will confirm that I can
add and then retrieve values from the Hashtable—a simple test, but one
that showcases the capabilities of NUnit. There are a number of test
types, as well as various Assert methods, that can be used to test every
part of your code.
To run this
test, I'll need to build the project, open the generated assembly in the
NUnit application, and then click the Run button. Figure 5
shows the results. I get a warm and fuzzy feeling when I see that big
green bar because it lets me know that the test passed. This simple
example shows how easy and powerful NUnit and unit testing can be. Being
able to write a unit test that can be saved and rerun whenever you
change code not only makes it easier for you to detect defects in your
code, but the result is that you can deliver better applications.
Figure 5 NUnit
NUnit is an open-source project that is
available for download from http://www.nunit.org
.
There is also an excellent NUnit Visual Studio .NET add-in which allows
you to run unit tests directly from Visual Studio. This can be found at
http://sourceforge.net/projects/nunitaddin
.
For more information on NUnit and its place in test-driven development,
see the article "Test-Driven
C#: Improve the Design and Flexibility of Your Project with Extreme
Programming Techniques
" in the April 2004 issue of MSDN
® Magazine
.
FxCop
The .NET Framework is very powerful, which
means there is great potential to create excellent applications, but
there is equal opportunity to create poor programs. FxCop is one of the
tools that can be used to help create better applications by enabling
you to examine an assembly and check it for compliance using a number of
different rules. FxCop comes with a set number of rules created by
Microsoft, but you can also create and include your own rules. For
instance, if you decided that all classes should have a default
constructor that takes no arguments, you could write a rule that checks
for a constructor on each class of an assembly. This way, no matter who
writes the code, you will have a certain level of consistency. If you
want more information on creating custom rules, see John Robbins' Bugslayer
column on the subject in the June 2004 issue of MSDN Magazine
.
So let's take a look at FxCop in action and
see what it finds wrong with the NUnitExample assembly that I have been
working with. When you open FxCop you first need to create an FxCop
project and then add to it the assembly that you want to test. Once the
assembly is added to the project, you can press Analyze, and FxCop will
examine the assembly. The errors and warning found in this assembly are
shown in Figure 6
.
Figure 6 Errors and Warning Found by FxCop
FxCop found a couple of
problems with my assembly. You can double-click on an error to see the
details, including a description of the rule and where you can find more
information. (Something you can do for fun is run FxCop on the
Framework assemblies and see what turns up.)
FxCop can help you create better, more
consistent code, but it cannot make up for poor application design or
just plain poor programming. FxCop is also not a replacement for peer
code review, but because it can catch a lot of errors before code
review, more time can be spent on serious issues rather than having to
worry about naming conventions.
FxCop
was developed by Microsoft and is available for download from http://www.gotdotnet.com/team/fxcop
.
Lutz Roeder's .NET Reflector
The next essential tool is called .NET
Reflector, which is a class browser and decompiler that can examine an
assembly and show you just about all of its secrets. The .NET Framework
introduced reflection which can be used to examine any .NET-based code,
whether it is a single class or an entire assembly. Reflection can also
be used to retrieve information about the various classes, methods, and
properties included in a particular assembly. Using .NET Reflector, you
can browse the classes and methods of an assembly, you can examine the
Microsoft intermediate language (MSIL) generated by these classes and
methods, and you can decompile the classes and methods and see the
equivalent in C# or Visual Basic® .NET.
To demonstrate the workings of .NET Reflector,
I am going to load and examine the NUnitExample assembly already shown.
Figure 7
shows this assembly loaded in .NET Reflector.
Figure 7 NUnitExample Assembly
Inside of .NET Reflector there are various
tools that you can use to examine this assembly further. To view the
MSIL that makes up a method, click on the method and select Disassembler
from the menu.
In addition to being
able to view the MSIL, you can also view the method as C# by selecting
Decompiler under the Tools menu. You could also view this method
decompiled to Visual Basic .NET or Delphi by changing your selection
under the Languages menu. Here is the code that .NET Reflector
generated:
The previous code looks very much like the code I actually wrote
for this method. Here is the actual code from this assembly:
Although there are some minor differences with the code, they
are functionally identical.
public void HashtableAddTest() { Hashtable hashtable1; hashtable1 = new Hashtable(); hashtable1.Add("Key1", "Value1"); hashtable1.Add("Key2", "Value2"); Assert.AreEqual("Value1", hashtable1["Key1"], "Wrong object returned!"); Assert.AreEqual("Value2", hashtable1["Key2"], "Wrong object returned!"); }
public void HashtableAddTest() { Hashtable ht = new Hashtable(); ht.Add("Key1", "Value1"); ht.Add("Key2", "Value2"); Assert.AreEqual("Value1", ht["Key1"], "Wrong object returned!"); Assert.AreEqual("Value2", ht["Key2"], "Wrong object returned!"); }
While
this example was a good way to show actual code versus decompiled code,
it does not represent what I consider to be the best use of .NET
Reflector, which is to examine .NET Framework assemblies and methods.
The .NET Framework offers many different ways to perform similar
operations. For example, if you need to read a set of data from XML,
there are a variety of different ways to do this using XmlDocument,
XPathNavigator, or XmlReader. By using .NET Reflector, you can see what
Microsoft used when writing the ReadXml method of the DataSet, or what
they did when reading data from the configuration files. .NET Reflector
is also an excellent way to see the best practices for creating objects
like HttpHandlers or configuration handlers because you get to see how
the team at Microsoft actually built those objects in the Framework.
.NET Reflector was written by Lutz Roeder and
can be downloaded from http://www.aisto.com/roeder/dotnet
.
NDoc
Code documentation is almost always a dreaded
task. I am not talking about the early design documents, or even the
more detailed design documents; I am talking about documenting
individual methods and properties on classes. The NDoc tool will
automatically generate documentation for your code using reflection to
examine the assembly and using the XML generated from your C# XML
comments. XML comments are only available for C#, but there is a Visual
Studio .NET Power Toy called VBCommenter which will do something similar
for Visual Basic .NET. In addition, the next release of Visual Studio
will support XML comments for more languages.
With NDoc you are technically still
documenting your code, but you are documenting as you write it (in the
XML comments), which is much easier to swallow. The first step when
using NDoc is to turn on XML comments generation for your assembly.
Right-click the project and select Properties | Configuration Properties
| Build, then enter a path in which to save the XML file in the XML
Documentation File option. When the project is built, an XML file will
be created with all of the XML comments included. Here is a look at a
method from the NUnit example documented with XML:
/// <summary> /// This test adds a number of values to the Hashtable collection /// and then retrieves those values and checks if they match. /// </summary> [Test] public void HashtableAddTest() { //Method Body Here }
The XML documentation on
this method will be extracted and saved in the XML file, shown here:
NDoc uses reflection to look at your assembly, then reads the
XML in this document, and matches them up. NDoc uses this data to create
any number of different documentation formats, including HTML help
files (CHMs). After generating the XML file, the next step is to load
the assembly and the XML file into NDoc so they can be processed. This
is done simply by opening NDoc and clicking the Add button.
<member name="M:NUnitExample.HashtableTest.HashtableAddTest"> <summary>This test adds a number of values to the Hashtable collection and then retrieves those values and checks if they match.</summary> </member>
Once the assembly and XML file are loaded into
NDoc and after you customize the output using the range of properties
available, clicking on the Generate button will start the process of
generating the documentation. Using the default properties, NDoc
generates some very attractive and functional .html and .chm files,
thereby automating in a quick and efficient manner what would otherwise
be a tedious task.
NDoc is an open
source project and can be downloaded from http://ndoc.sourceforge.net
.
NAnt
NAnt is a .NET-based build tool that, unlike
the current version of Visual Studio .NET, makes it easy to create a
build process for your project. When you have a large number of
developers working on a single project, you can't rely on the build from
a single user's box. You also do not want to have to build the project
manually on a regular basis. Instead, you create an automated build
process that runs every night. NAnt allows you to build your solution,
copy files, run NUnit tests, send e-mail, and much more. Unfortunately,
NAnt is lacking a nice looking graphical interface, but it does have a
console application and XML files that specify which tasks should be
completed during the build process. Note that MSBuild, the new build
platform that's part of Visual Studio 2005, provides for very robust
build scenarios and is similarly driven by XML-based project files.
NAnt in Action
In this example I am going to create an NAnt
build file for the NUnitExample solution that I created earlier. First I
need to create an XML file with the .build extension, place it in the
root of my project, and then add an XML declaration to the top of the
file. The first tag I need to add to the file is the project tag:
The project tag is also used to set the name of the project, the
default target, and the base directory. The description tag is used to
set a brief description of this project.
<?xml version="1.0"?> <project name="NUnit Example" default="build" basedir="."> <description>The NUnit Example Project</description> </project>
Next, I'll add the property tag, which can be
used to store a setting in a single location that can then be accessed
from anywhere in the file. In this case, I am going to create a property
called debug, which I can then set to true or false, reflecting whether
or not I want the project to be compiled in the debug configuration.
(In the end, this particular property does not actually affect how the
project will be built; it is simply a variable that you set and which
will be read from later when you are actually determining how to build
the project.)
Next, I need to create
a target tag. A project can contain multiple targets which can be
specified when NAnt is run. If no target is specified, the default is
used, which I set in the project element. In this example, the default
target is build. Let's take a look at the target element, which will
contain the majority of the build info:
Inside the target element, I am going to set the name of the
target to build and create a description of what this target will do.
I'll also create a csc element, which is used to specify what should be
passed to the csc C# compiler. Let's take a look at the csc element:
<target name="build" description="compiles the source code"> </target>
<csc target="library" output="./bin/debug/NUnitExample.dll" debug="${debug}"> <references> <includes name="C:/program files/NUnit V2.1/bin/NUnit.Framework.dll"/> </references> <sources> <includes name="HashtableTest.cs"/> </sources> </csc>
First, I have to set the
target of the csc element. In this case I will be creating a .dll file
so I set the target to library. Next, I have to set the output of the
csc element, which is where the .dll file will be created. Finally, I
need to set the debug property, which determines whether the project
will be compiled in debug. Since I created a property earlier to store
this value, I can use the following string to access the value of that
property: ${debug}. The csc element also contains a number of
sub-elements. I need to create two elements: the references element will
tell NAnt which assemblies I need to reference for this project, and
the sources element will tell NAnt which files to include in the build.
In this example, I reference the NUnit.Framework.dll assembly and
include the HashtableTest.cs file. The complete build file is shown in Figure
8
. (You would normally also create a clean target that would
be used to delete the generated files, but I have omitted it for the
sake of brevity.)
Figure 8 NAnt Build File
<?xml version="1.0"?> <project name="NUnit Example" default="build" basedir="."> <description>The NUnit Example Project</description> <property name="debug" value="true"/> <target name="build" description="compiles the source code"> <csc target="library" output="./bin/debug/NUnitExample.dll" debug="${debug}"> <references> <includes name="C:/program files/NUnit V2.1/bin/NUnit.Framework.dll" /> </references> <sources> <includes name="HashtableTest.cs"/> </sources> </csc> </target> </project>
To build this file I need to
go to the root directory of my project, where the build file is located,
and execute nant.exe from that location. If the build is successful,
you can find the .dll and .pdb file in the bin directory of this
application. While using NAnt is definitely not as easy as clicking
Build in Visual Studio, it is a very powerful tool for developing a
build process that runs on an automated schedule. NAnt also includes
helpful features such as the ability to run unit tests or copy
additional files (features that are not supported by the current Visual
Studio build process).
NAnt is an
open source project and can be downloaded from http://sourceforge.net/projects/nant
.
Switch Tools
I have lumped together two separate tools
under the heading Switch Tools. These two tools are rather simple, but
can be extremely useful. The first is the ASP.NET Version Switcher,
which can be used to switch the version of ASP.NET that a virtual
directory is running under. The second tool is the Visual Studio
Converter, which can be used to switch a project file from Visual Studio
.NET 2002 to Visual Studio .NET 2003.
When IIS handles a request, it looks at the
extension of the file that is being requested, and then based on the
extension mappings for that Web site or virtual directory, it either
delegates the request to an ISAPI extension or handles it itself. This
is how ASP.NET works; extension mappings are registered for all of the
ASP.NET extensions and directs them to the aspnet_isapi.dll. This works
flawlessly until you install ASP.NET 1.1, which upgrades the extension
mapping to the new version of aspnet_isapi.dll. This causes errors when
an application built on ASP.NET 1.0 tries to run with version 1.1. To
fix this, you can switch all of the extension mappings back to the 1.0
version of aspnet_isapi.dll, but with 18 extension mappings it is not a
lot of fun to do this by hand. This is where the ASP.NET Version
Switcher becomes useful. This small utility can be used to switch the
version of the .NET Framework that any single ASP.NET application is
using.
Figure 9 ASP.NET Version
Switcher
Figure
9
shows the ASP.NET Version Switcher in action. Using it is as
simple as selecting the application and then selecting the version of
the .NET Framework that you would like the application to use. The tool
then uses the aspnet_regiis.exe command-line tool to switch the
application to the selected version of the Framework. This tool will
become even more useful as future versions of ASP.NET and the .NET
Framework are released.
ASP.NET
Version Switcher was written by Denis Bauer and is available for
download from http://www.denisbauer.com/NETTools/ASPNETVersionSwitcher.aspx
.
The Visual Studio .NET Project Converter (see
Figure 10
) is very similar to the ASP.NET Version
Switcher, except that it is used to switch the version of a Visual
Studio project file. Even though there is only a small difference
between versions 1.0 and 1.1 of the .NET Framework, once a project file
from Visual Studio .NET 2002 is converted to Visual Studio .NET 2003, it
cannot be converted back. While this might not be an issue most of the
time (since there are few breaking changes between the .NET Framework
versions 1.0 and 1.1), at some point you may need to switch a project
back. This converter can convert any solution or project file from
Visual Studio 7.1 (Visual Studio .NET 2003) to Visual Studio 7.0 (Visual
Studio .NET 2002), and back if necessary.
Figure 10 Visual Studio .NET
Project Converter
The
Visual Studio .NET Project Converter was written by Dacris Software. It
is available for download from http://www.codeproject.com/macro/vsconvert.asp
.
Conclusion
This has been a bit of a whirlwind tour of
these tools, but I have tried to present you with at least enough
information to whet your appetite. I trust that this article has
provided you with some insight into a couple of free tools that you can
start using right away to write better projects. I also urge you to make
sure you have all the other right tools available to you, whether it is
newest version of Visual Studio, a powerful computer, or a free
utility. Having the right tools can make all the difference.
作者:Angelo Lee
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.