Browse DevX
Sign up for e-mail newsletters from DevX


What's New in .NET 2.0 for Assemblies and Versioning? : Page 2

.NET 2.0 and Visual Studio 2005 have numerous innovations regarding assemblies and versioning. You can add a reference to an EXE assembly, resolve type conflicts by aliasing a reference, given permission, you can access the internal types of another assembly, protect and manage with ease your strong name keys, insist on building against a specific version of an assembly, and target specific CPU architectures.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Reference Aliasing
In .NET 2.0, by default, all types are rooted in a namespace called global. For example, this class definition...

class MyClass {}

...is identical to this one:

namespace global { class MyClass {} }

You don't need to explicitly use the global namespace since it is always implied by default. The global namespace is instrumental in resolving type name conflicts, especially when multiple assemblies are involved.

When you add an assembly reference, it is possible to create a conflict with another type already defined by your application in another assembly it references. For example, consider the MyApplication.exe and MyLibrary.dll assemblies, which both define the class MyClass in the namespace MyNamespace.

//In MyApplication.exe namespace MyNamespace { public class MyClass {...} } //In MyLibrary.dll namespace MyNamespace { public class MyClass {...} }

Each definition of MyClass is completely distinct, providing different methods and behaviors. If you add a reference to MyLibrary.dll from within MyApplication.exe, when you try to use the type MyClass like so:.

using MyNamespace; MyClass obj = new MyClass();

The compiler will issue an error because it does not know how to resolve it—that is, it doesn't know which definition of MyClass to reference.

C# 2.0 allows you to resolve type name conflicts by aliasing the assembly reference. By default, all namespaces are rooted in the global namespace. When aliasing an assembly, the namespaces used in that assembly will be resolved under the alias, not globally. To alias an assembly, first add a reference to it in Visual Studio 2005. Next, expand the Reference folder in the Solution Explorer, and display the properties of the referenced assembly (see Figure 2).

Figure 2: Aliasing an assembly reference.
If you added the reference by browsing to the assembly, the Aliases property will be set explicitly to global. If you added the reference by selecting the assembly from the Projects tab, the Aliases value will be empty (but implicitly global). You can specify multiple aliases, but for addressing most conflicts a single alias will do (unless you also have conflicts with other aliases).

Next, add as the first line of the file the extern alias directive, instructing the compiler to include the types from the alias in the search path.

extern alias MyLibraryAlias;

You can now refer to the class MyClass from MyLibrary.dll.

MyLibraryAlias::MyNamespace.MyClass obj; obj = new MyLibraryAlias::MyNamespace.MyClass();

Note that the extern alias directive must appear before any using directives, and that all types in the MyLibrary.dll can only be referred to via the alias, because these types are not imported to the global scope.

Using aliases and fully qualified namespaces may result in exceedingly long lines. As shorthand, you can also alias the fully qualified name.

C# 2.0 allows you to resolve type name conflicts by aliasing the assembly reference.

using MyLibrary = MyLibraryAlias::MyNamespace; MyLibrary.MyClass obj; obj = new MyLibrary.MyClass();

Friend Assemblies
An interesting assembly-level attribute introduced by .NET 2.0 is the InternalsVisibleTo attribute, defined in the System.Runtime.CompilerServices namespace. The InternalsVisibleTo attribute allows you to expose internal types and methods to clients from another specified assembly. This is also known as declaring a friend assembly. For example, suppose the server assembly MyClassLibrary.dll defines the internal class MyInternalClass as.

internal class MyInternalClass { public void MyPublicMethod() {...} internal void MyInternalMethod() {...} }

Suppose you add the following line to the AssemblyInfo.cs file of MyClassLibrary.dll.

[assembly: InternalsVisibleTo("MyClient")]

Now any client in the assembly MyClient.dll and MyClient.exe can use MyInternalClass and call its public or internal members. In addition, any sub class in the MyClient assembly can access members marked as protected internal. Declaring an assembly as a friend could easily be abused and violate the essential encapsulation of the internals of the assembly, and tightly couple the client to the internals of the server assembly. Declaring a friend assembly is available for when you break an existing assembly into one or more assemblies by moving some of the types to new assemblies. If the relocated types still rely on internal types in the original assembly, declaring a friend assembly is a quick (albeit potentially dirty) way of enabling the move. Another case where a friend assembly is handy is when you want to test internal components but the text client resides in different assembly.

The InternalsVisibleTo attribute allows you to expose internal types and methods to clients from another specified assembly.
When you apply the InternalsVisibleTo attribute like so:

[assembly: InternalsVisibleTo("MyClient")]

It does not matter whether the MyClient assembly has a friendly name or a strong name in both cases it is allowed access to the internals of the server. Obviously this is not a very secure or safe way of exposing your internal types. Using the InternalsVisibleTo attribute should be restricted to assemblies developed in conjunction with the server assembly. All a third-party assembly has to do to access the server assembly internals is to change its friendly name. In addition, this use of the InternalsVisibleTo attribute is version indifferent—any version of the MyClient assembly can access the server internals.

To provide the additional degree of security to the InternalsVisibleTo attribute, the server assembly can also specify the token of the public key of the client assembly.

[assembly:InternalsVisibleTo( "MyClient,PublicKeyToken=745901a54f88909b")]

When specifying the public key token, the server assembly grants permission to access its internals only for client assemblies with a matching friendly and strong name, and the client-side compiler enforces that.

In addition to the friendly name of the client assembly, the server assembly can specify a version number.

[assembly:InternalsVisibleTo( "MyClient,Version=")]

The effect of the version number depends on whether or not the client assembly has a strong name.

If the client assembly has only a friendly name, then the version stipulation has no affect, and such clients can freely access the internals of the server assembly. If, however, the client does have a strong name (any strong name), then the client-side compiler will insist that only a client assembly with a matching strong name and version number can access the internals. Note that this behavior is akin to reverse-versioning, where the server constrains the version of its clients.

Of course, the server assembly can specify both a version number and a public key token.

[assembly:InternalsVisibleTo( "MyClient,Version=, PublicKeyToken=745901a54f88909b")]

In which case, only clients with a matching strong name and version number can access the internals.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date