One of the features missing from Visual Basic 2005 was support for what is known as friend assemblies. We are currently working on enabling Friend Assemblies for the next version of Visual Basic (code named Orcas). I wanted to share with you some of the nice things about friend assemblies and some of the gotchas. Much of the discussion applies equally well to C#, which supported friend assemblies in Visual C# 2005.
Declaring another Assembly as your friend
To declare another assembly A as your friend, simply insert the attribute InternalsVisibleTo(“A”). I recommend that you put this in assemblyinfo.vb, where the rest of the assembly attributes go.
One of the rules regarding friend assemblies is that if the declaring assembly is a strong name assembly (that is, that it specifies a key file/container), then you can only declare friends that are also strong name assemblies. This means that you must specify the entire 128 bit public key in the InternalsVisibleTo attribute: InternalsVisibleTo(“A, PublicKey=<128-bit Key>”).
This prevents a strong name assembly from declaring friendship to any random assembly “A”, and having any weak named (thus un-trusted) assembly named “A” reference your friend data.
See the following for more information on InternalsVisibleTo attribute.
NUnit, Unit tests, and Friend Assemblies
One of the scenarios enabled by such the friend assemblies feature is unit testing with NUnit (and other TDD frameworks that rely on being able to see privates). Note that VSTS does not rely on this feature, and VSTS code gens reflection stubs to get access to friend/private data.
This means that with Visual Basic Orcas, you can define your assembly A, and a test assembly TestA, and in assembly A, you declare TestA your friend, and TestA will now be able to access the friend members of A.
This attribute tells the compiler to emit code that makes TestA, with the public key MyKey, your friend. This will instruct the CLR to allow TestA to access friend members and fields in your assembly.
Inheritance and Friend Assemblies
Inheritance provides a unique problem for friend assemblies. Without friend assemblies, the scoping rules of classes (Public, Friend, or Private) are applied throughout the inheritance chain. Consider the following example, all classes defined in the same assembly:
Public Class A
Friend Overridable Sub Foo()
Public Class B : Inherits A
Friend Overrides NotOverridable Sub Foo()
Public Class C : Inherits B
Friend Overrides Sub Foo()
In this case, the compiler will generate an error in class C because Foo is trying to override a method that was marked NotOverridable. So far, so good.
Now what if these classes were defined in three assemblies, A, B, and C (the names correspond to the classes)? Now, class B in assembly B cannot override Foo since it cannot see the Foo declared Friend in class A in assembly A.
But now, with friend assemblies, it is possible for assembly A to declare both assembly B and C as its friend. So now, class B can override Foo in class A, since it can see it. But what happens to class C? Class C in assembly C doesn’t see the friend method in assembly B, and so it happily overrides Foo…oops. This will throw an exception at runtime, since B was “in the inheritance chain” but C, lower in the chain, could not see all the members it needed to see!
This is a unique problem introduced by friend assemblies.
What we did in the compiler was to dig through the class hierarchy and ensure that no hidden classes were changing the overriding semantics, and to generate a compiler error in this scenario.
There are other scenarios with the same idea; you can imagine that a hidden class seals a method but then provides another overridable method of the same name. I think we have taken care of all of these issues in the Visual Basic Orcas compiler; feel free to give the Visual Studio Orcas CTP a try and let me know if it doesnt work as you expect!