Monday, March 27, 2006 5:49 PM bart

Friend assemblies explained

To continue my evangelism of .NET Framework 2.0 features, I'm going to focus on friend assemblies in today's post.

The old story

Go back to the .NET Framework 1.x era and think of the various visibility modifiers you can apply on types and members. One of those visibility modifiers is internal which basically states that the type (let's stick with types) is only visible inside the assembly to which it belongs. This is very useful in quite some scenarios where you need some helper types to aid in the internal workings of an assembly containing some functionality. However, the scalabilty of internal in .NET Framework 1.x is rather limited because you can't span "internal types" across multiple assemblies which are created by various teams. As the matter in fact, you'd like to group a series of assemblies an make them all "friends" living together in peace, relying on each other's internal types. That's exactly what you need if you start writing software in group and you decide that various teams will be responsible to create some helper (and other have-to-be-internal) types but it's infeasible to make them work on the same assembly (or if you just want to divide the internal types across mutliple assemblies for any reason whatsoever).

Now, how does this look like? The only way I'm aware of to get around this problem using .NET FX 1.x is by using Code Access Security (CAS), more specifically the StrongNameIdentityPermission class which can be used to demand that the caller's identitty matches a given identity (based on a strong name which you should be familiar with). In code you'll get the following:

[StrongNameIdentityPermission(SecurityAction.LinkDemand,
PublicKey=
"
0024000004800000940000000602000000240000525341310
00400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9
e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9
ad236132102900b723cf980957fc4e177108fc607774f29e8
320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d6
22caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a
12436518206dc093344d5ad293"
)]
public class
ProtectedUtility //only callable by my "friends"
{

   ...
}

<Game>Find the public key owner.</Game>

This means that only assemblies with the given public key will be able to use the functionality defined in ProtectedUtility. Notice additional properties can be set on the StrongNameIdentityPermission attribute, such as Name and Version.

 

The new "friendly" story

.NET Framework 2.0 frees you from this burden of having to declare a bunch of StrongNameIdentityPermissions to accomplish the aforementioned goal. Now, the concept of "friend assemblies" is known by the runtime and you as a developer can take advantage of this. All you have to do now is to change the AssemblyInfo.cs file (well, as the matter in fact, you can put the attribute I'm going to show you everywhere but in VS2005 solutions the AssemblyInfo.cs file is the most convenient place to put it in) and add the following attribute:

[assembly: InternalsVisibleTo("SomeBodySpecial,
0024000004800000940000000602000000240000525341310
00400000100010007d1fa57c4aed9f0a32e84aa0faefd0de9
e8fd6aec8f87fb03766c834c99921eb23be79ad9d5dcc1dd9
ad236132102900b723cf980957fc4e177108fc607774f29e8
320e92ea05ece4e821c0a5efe8f1645c4c0c93c1ab99285d6
22caa652c1dfad63d745d6f2de5f17e5eaf0fc4963d261c8a
12436518206dc093344d5ad293
"
)]

The difference with the oldfashioned way to do this is of course the fact that this attribute applies to the whole assembly and every internal type in it, rather than the explicitly marked types. Also notice you don't have to declare the members you want to make visible outside the assembly as public (which was a need to work with StrongNameIdentittyPermission). One little caveat is that you need to specify the /out: compiler parameter when compiling the friend assembly (that's the one which is referring to the assembly that "exports" internal types in the way outlined above) so the system knows the identity upfront in order to determine whether it's a friend or not. Luckily, VS2005 takes care of this (by design).

There is however one situation in which the old way of working still is the better way to do this kind of stuff and that's when the assemblies don't ship together and you want to avoid compatibility problems. In such a case a publicly visible type is the better choice, restricted in its usage by means of a StrongNameIdentityPermission link demand.

A few remarks to conclude:

  • The syntax of the InternalsVisibleTo parameter has to be name, public key. A public key token is not allowed and both elements have to be present in the declaration (name and public key). Note: you can retrieve the public key of a strong-named assembly using sn -Tp <assembly>.
  • Friends of a strongly-named assemblies have to be strongly-named too.
  • Of course, InternalsVisibleTo means exactly what it tells: it only exposes types and members declared as internal to the friends.
  • You can declare more than one InternalsVisibleTo per assembly, whileas it's not possible to have more than one StrongNameIdentityPermission on a type.
  • Friend declarations are explicit-only and are not inferred. Therefore, the is no transitive property ("the friends of my friends are my friends" doesn't hold in this context, luckily!).
  • Friendship is a non-commutative relationship. If A is a friend of B, then B is not automatically a friend of A. Whether this matches the real world is a filosophical question which falls outside my domain of expertise :-).
Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: ,

Comments

# re: Friend assemblies explained

Friday, May 05, 2006 12:09 PM by SriverFX

Any thoughts on how to make an assembly a friend of a Web project's output? (say, a set of resources?)

# Visual Basic 9.0 Feature Focus - Friend Assemblies

Thursday, October 11, 2007 3:31 PM by Elan Hasson's Favorite Blogs

Welcome back to the Visual Basic 9.0 Feature Focus blog series. In this post, we'll cover Friend