8.1. Assemblies

A namespace allows us to neatly package the types defined within our component, ensuring that others can safely include our component within their program without any existing code breaking because of unexpected name conflicts. This is a very important guarantee.

Under Visual Studio.NET, the unit of program deployment is the assembly. An assembly serves as a kind of component DNA. It is a self-contained, versionable, self-describing unit of a .NET application. It contains all the executable code we've written, of course. This code is stored in an intermediate language. We'll talk about that a bit later. In addition, it holds metadata about the types within our program, and the environment in which the program was built. This information is generated during the compilation and build of our code. It also contains a manifest. The manifest describes the content of the assembly—what is necessary for execution: dependencies, versioning, and security.

Assemblies are loaded on demand. That is, the runtime environment loads the assembly only when our program makes use of a type or associated metadata about the type contained within the assembly. This is when the type's static constructor is executed.

We can access a runtime representation of both our executing assembly and each assembly associated with our program. There are several different ways to do this, depending on how general or specific our retrieval needs are.

What if we need access to all the assemblies associated with our application? First we need to gain access to a representation of the currently executing thread. We access it through the CurrentDomain property of the AppDomain class. An app domain acts as the container for a process. Assemblies are loaded into an app domain. The following code sequence retrieves all the assemblies associated with the current thread as an array of Assembly class objects:

AppDomain theApp = AppDomain.CurrentDomain;
Assembly [] currAssemblies = theApp.GetAssemblies();

Console.WriteLine( "The current AppDomain has {0} Assemblies",
                    currAssemblies.Length );

In order for this code to compile, we must use the System.Reflection namespace, which is where the Assembly class is defined (we'll come back to the Reflection namespace in Section 8.2):

using System.Reflection

To retrieve only the Assembly object from which our code is executing, we invoke the static GetExecutingAssembly() method of the Assembly class:

Assembly myAssembly = Assembly.GetExecutingAssembly();

Alternatively, to retrieve the Assembly object within which a particular type is defined, we invoke the static GetAssembly() method of the Assembly class, passing it the Type object associated with the type.

There are two ways to retrieve a Type object. If we have an object of the type, we simply invoke its inherited GetType() method. Alternatively, we can invoke the static GetType() method of the Type class, passing it a string holding the name of the type:

Object o = new Fibonacci();
Assembly fibAssem = Assembly.GetAssembly( o.GetType() );

Type     calType  = Type.GetType( "JulianCalendar" );
Assembly calAssem = Assembly.GetAssembly( calType );

We can learn nearly everything that we might wish to know about our component through the Assembly class. To retrieve information about the entry point of the Assembly object, if it has one, we query the EntryPoint property of the Assembly class:

MethodInfo ep = fibAssem.EntryPoint;

The MethodInfo class contains detailed information about the method to which it refers, such as its name, access level, return type, parameter list, and so on. It is defined within the System.Reflection namespace. We'll look at it further in Section 8.2.

To retrieve all the types defined within the assembly, we invoke the GetTypes() method:

Type [] types = fibAssem.GetTypes();

Alternatively, we can request a specific type by name through the GetType() method:

Type fibType = fibAssem.GetType( "Fibonacci" );

If the type is not found, GetType() returns null. If we prefer that it throw an exception, we pass in a literal true value as a second argument:

Type fibType = fibAssem.GetType( "Fibonacci", true );

For example, the following output:

The current AppDomain has 2 Assemblies

The Assembly FullName Property:
    mscorlib, Ver=1.0.2204.21, Loc="", SN=03689116d3a4ae33

assembly name:    mscorlib
manifest loc:     C:/WINNT/Microsoft.NET/Framework/v1.0.2204/
mscorlib.dll
entry point:      Not Defined
number of types:  1205

The Assembly FullName Property:
    Assemblies, Ver=1.0.471.38017, Loc=""

assembly name:    Assemblies
manifest loc:     C:/C#Programs/Assemblies/bin/Debug/
Assemblies.exe
entry point:      Void Main()
number of types:  2
The types contained within the assembly are:
        type name:  AssemblyEntryPoint
        type name:  AssemblyExplore

is generated from the following method:

public static void getAssemblies()
{
   AppDomain theApp = AppDomain.CurrentDomain;
   Assembly [] currAssemblies = theApp.GetAssemblies();

   Console.WriteLine( "The current AppDomain has {0} Assemblies",
                          currAssemblies.Length );

   foreach ( Assembly a in currAssemblies )
   {
      message( "
The Assembly FullName Property: 
	",
                 a.FullName );
   int     pos  = a.FullName.IndexOf( ',' );
   string  name = a.FullName.Substring(0, pos);
   Type [] t    = a.GetTypes();

   string     manifestLocation = a.Location;
   MethodInfo theEntry         = a.EntryPoint;

   message( "assembly name:   ", name );
   message( "manifest loc:    ", manifestLocation );
   message( "entry point:     ",
                 theEntry != null
                        ? theEntry.ToString():"Not Defined");

   message( "number of types: ", t.Length );

   if ( t.Length < 10 )
   {
          Console.WriteLine(
                "The types contained within the assembly are:");

          foreach ( Type tt in t )
                message( "	type name: ", tt.Name );
      }
}

We can use the static Load() or LoadFrom() method of the Assembly class to explicitly load an assembly. To create an instance of a type defined within the Assembly object, we can use the CreateInstance() method. We'll see an example in our discussion of reflection in the next section.

There are quite a number of administrative issues with assemblies that I won't cover, such as whether to package the assembly in a single module or in multiple modules, whether the assembly should be private or shared, and versioning and security issues. By default, Visual Studio generates a private assembly in a single module.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.122.68