
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
472 CHAPTER 14 ■ AN INTRODUCTION TO LINQ
Summary
LINQ is a set of related technologies that attempts to provide a single, symmetrical manner to interact with diverse forms of data. As explained over the course of this chapter, LINQ can interact with any type implementing the IEnumerable<T> interface, including simple arrays as well as generic and nongeneric collections of data.
As you have seen over the course of this chapter, working with LINQ technologies is accomplished using several new C# 2008 language features. For example, given the fact that LINQ query expressions can return any number of result sets, it is common to make use of the var keyword to represent the underlying data type. As well, lambda expressions, object initialization syntax, and anonymous types can all be used to build very functional and compact LINQ queries.
More importantly, you have seen how the C# LINQ query operators are simply shorthand notations for making calls on static members of the System.Linq.Enumerable type. As shown, most members of Enumerable operate on Func<T> delegate types, which can take literal method addresses, anonymous methods, or lambda expressions as input to evaluate the query.

P A R T 4
Programming with .NET Assemblies




CHAPTER 15 ■ INTRODUCING .NET ASSEMBLIES |
477 |
// Note we are not "using" MyShapes anymore. using System;
namespace MyApp
{
class ShapeTester
{
static void Main(string[] args)
{
MyShapes.Hexagon h = new MyShapes.Hexagon(); MyShapes.Circle c = new MyShapes.Circle(); MyShapes.Square s = new MyShapes.Square();
}
}
}
Typically there is no need to use a fully qualified name. Not only does it require a greater number of keystrokes, but also it makes no difference whatsoever in terms of code size or execution speed. In fact, in CIL code, types are always defined with the fully qualified name. In this light, the C# using keyword is simply a typing time-saver.
However, fully qualified names can be very helpful (and sometimes necessary) to avoid name clashes that may occur when using multiple namespaces that contain identically named types. Assume you have a new namespace termed My3DShapes, which defines three classes capable of rendering a shape in stunning 3D:
// Another shapes namespace...
using System;
namespace My3DShapes
{
//3D Circle class class Circle{ }
//3D Hexagon class class Hexagon{ }
//3D Square class class Square{ }
}
If you update ShapeTester as was done here, you are issued a number of compile-time errors, because both namespaces define identically named types:
// Ambiguities abound! using System;
using MyShapes; using My3DShapes;
namespace MyApp
{
class ShapeTester
{
static void Main(string[] args)
{
// Which namespace do I reference?
Hexagon h = new Hexagon(); // Compiler error!
Circle |
c |
= |
new |
Circle(); |
// |
Compiler |
error! |
Square |
s |
= |
new |
Square(); |
// |
Compiler |
error! |
}
}
}

478 CHAPTER 15 ■ INTRODUCING .NET ASSEMBLIES
The ambiguity can be resolved using the type’s fully qualified name:
// We have now resolved the ambiguity. static void Main(string[] args)
{
My3DShapes.Hexagon h = new My3DShapes.Hexagon(); My3DShapes.Circle c = new My3DShapes.Circle(); MyShapes.Square s = new MyShapes.Square();
}
Defining using Aliases
The C# using keyword can also be used to create an alias to a type’s fully qualified name. When you do so, you are able to define a token that is substituted with the type’s full name at compile time, for example:
using System; using MyShapes; using My3DShapes;
// Resolve the ambiguity using a custom alias. using The3DHexagon = My3DShapes.Hexagon;
namespace MyApp
{
class ShapeTester
{
static void Main(string[] args)
{
// This is really creating a My3DShapes.Hexagon type.
The3DHexagon h2 = new The3DHexagon();
...
}
}
}
This alternative using syntax can also be used to create an alias to a lengthy namespace. One of the longer namespaces in the base class library would have to be System.Runtime.Serialization. Formatters.Binary, which contains a member named BinaryFormatter. If you wish, you could create an instance of the BinaryFormatter as follows:
using MyAlias = System.Runtime.Serialization.Formatters.Binary;
namespace MyApp
{
class ShapeTester
{
static void Main(string[] args)
{
MyAlias.BinaryFormatter b = new MyAlias.BinaryFormatter();
}
}
}
as well as with a traditional using directive:

CHAPTER 15 ■ INTRODUCING .NET ASSEMBLIES |
479 |

480 CHAPTER 15 ■ INTRODUCING .NET ASSEMBLIES
Given that you have now nested the My3DShapes namespace within the Chapter15 root namespace, you need to update any existing using directives and type aliases:
using Chapter15.My3DShapes;
using The3DHexagon = Chapter15.My3DShapes.Hexagon;
The “Default Namespace” of Visual Studio 2008
On a final namespace-related note, it is worth pointing out that by default, when you create a new C# project using Visual Studio 2008, the name of your application’s default namespace will be identical to the project name. From this point on, when you insert new items using the Project Add New Item menu selection, types will automatically be wrapped within the default namespace. If you wish to change the name of the default namespace (e.g., to be your company name), simply access the Default namespace option using the Application tab of the project’s Properties window (see Figure 15-1).
Figure 15-1. Configuring the default namespace
With this update, any new item inserted into the project will be wrapped within the IntertechTraining namespace (and, obviously, if another namespace wishes to use these types, the correct using directive must be applied).
■Source Code The Namespaces project is located under the Chapter 15 subdirectory.
The Role of .NET Assemblies
.NET applications are constructed by piecing together any number of assemblies. Simply put, an assembly is a versioned, self-describing binary file hosted by the CLR. Now, despite the fact that
.NET assemblies have exactly the same file extensions (*.exe or *.dll) as previous Win32 binaries (including legacy COM servers), they have very little in common under the hood. Thus, to set the stage for the information to come, let’s consider some of the benefits provided by the assembly format.

CHAPTER 15 ■ INTRODUCING .NET ASSEMBLIES |
481 |
Assemblies Promote Code Reuse
As you have been building your Console Applications over the previous chapters, it may have seemed that all of the applications’ functionality was contained within the executable assembly you were constructing. In reality, your applications were leveraging numerous types contained within the always accessible .NET code library, mscorlib.dll (recall that the C# compiler references mscorlib.dll automatically), and in the case of some examples, System.Windows.Forms.dll.
As you may know, a code library (also termed a class library) is a *.dll that contains types intended to be used by external applications. When you are creating executable assemblies, you will no doubt be leveraging numerous system-supplied and custom code libraries as you create the application at hand. Do be aware, however, that a code library need not take a *.dll file extension. It is perfectly possible for an executable assembly to make use of types defined within an external executable file. In this light, a referenced *.exe can also be considered a “code library.”
Regardless of how a code library is packaged, the .NET platform allows you to reuse types in a language-independent manner. For example, you could create a code library in C# and reuse that library in any other .NET programming language. It is possible to not only allocate types across languages, but also derive from them. A base class defined in C# could be extended by a class authored in Visual Basic. Interfaces defined in Pascal .NET can be implemented by structures defined in C#, and so forth. The point is that when you begin to break apart a single monolithic executable into numerous .NET assemblies, you achieve a language-neutral form of code reuse.
Assemblies Establish a Type Boundary
To begin this chapter, you learned about the formalities behind .NET namespaces. Recall that a type’s fully qualified name is composed by prefixing the type’s namespace (e.g., System) to its name (e.g., Console). Strictly speaking however, the assembly in which a type resides further establishes a type’s identity. For example, if you have two uniquely named assemblies (say, MyCars.dll and
YourCars.dll) that both define a namespace (CarLibrary) containing a class named SportsCar, they are considered unique types in the .NET universe.
Assemblies Are Versionable Units
.NET assemblies are assigned a four-part numerical version number of the form <major>.<minor>. <build>.<revision> (if you do not explicitly provide a version number, the assembly is automatically assigned a version of 0.0.0.0). This number, in conjunction with an optional public key value, allows multiple versions of the same assembly to coexist in harmony on a single machine. Formally speaking, assemblies that provide public key information are termed strongly named. As you will see in this chapter, using a strong name, the CLR is able to ensure that the correct version of an assembly is loaded on behalf of the calling client.
Assemblies Are Self-Describing
Assemblies are regarded as self-describing in part because they record every external assembly it must have access to in order to function correctly. Thus, if your assembly requires System.Windows. Forms.dll and System.Drawing.dll, they will be documented in the assembly’s manifest. Recall from Chapter 1 that a manifest is a blob of metadata that describes the assembly itself (name, version, required external assemblies, etc.).
In addition to manifest data, an assembly contains metadata that describes the composition (member names, implemented interfaces, base classes, constructors, and so forth) of every contained type. Given that an assembly is documented in such vivid detail, the CLR does not consult