BlackWasp
C# Programming
.NET 2.0+

C# Friend Assemblies

Occasionally you may want to access internal classes and their members found in one assembly from code in a separate assembly. The 'internal' modifier prevents this type of access. This restriction can be circumvented using C# 2.0 friend assemblies.

The Internal Modifier

The 'internal' access modifier can be applied to classes and their members to restrict their visibility to external objects. Internal items are available outside of the class but only to other items within the same assembly. Classes in other assemblies are generally unaware of the internal elements.

Sometimes you may wish to remove this restriction, allowing the internal classes and members in one assembly to be visible to other, specific assemblies. You may want to do this to split a large assembly into several more manageable parts or to allow testing of internal items from a separate automated testing assembly. This can be achieved using friend assemblies.

NB: The use of friend assemblies is good for testing purposes. If you use friend assemblies for other purposes, you should carefully consider your design to ensure that you are not breaking the rules of encapsulation.

Creating a Friend Assembly

Friend assemblies are created by adding an assembly attribute to the code. This attribute specifies the name of another assembly that will be granted access to the internal members as if they were public. Any assemblies not specified will still be prevented from accessing the internal items. All private and protected classes and members will remain inaccessible.

To demonstrate, create a new, blank solution in Visual Studio named "FriendAssemblyDemo". Add two projects to this project. The first should be a console application named "Caller" and the second should be a class library called "InternalsVisible". The Caller application will be accessing an internal class in the InternalsVisible assembly. To link the two assemblies, add a reference to the class library within the console application.

We can now add an internal class to the InternalsVisible project. Modify the automatically created class file to provide the following code for the class:

internal class World
{
    internal void ShowMessage()
    {
        Console.WriteLine("Hello, world");
    }
}

Next, modify the program class in the console application as shown below:

class Program
{
    static void Main(string[] args)
    {
        InternalsVisible.World world = new InternalsVisible.World();
        world.ShowMessage();
    }
}

If you attempt to compile the solution, you will see a compiler error. This error indicates that the InternalsVisible.World class cannot be accessed due to its protection level. To specify that the internals of the InternalsVisible project should be available to the Caller application, we need to add the following attribute to the class library's assembly. This line should appear outside of any namespace or class declaration.

NB: This attribute is found in the System.Runtime.CompilerServices namespace so ensure that the code file in which it appears includes the line, using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Caller")]

You should now be able to compile and execute the solution, demonstrating that the Caller application does indeed access an internal method of an internal class in the InternalsVisible assembly.

Working with Signed Assemblies

If the assembly that contains the accessible internal items is signed with a public / private key pair, creating friend assemblies becomes more complicated. Firstly, you must also sign all of the assemblies that access its internal members. Secondly, the InternalsVisibleTo attribute must be modified to include the full public key, which is over three hundred characters in length. For example:

[assembly: InternalsVisibleTo("Caller, PublicKey=002400000480000094000000060200
0000240000525341310004000001000100655f087992abe9baf3d84d7bd066c98064b68633c1a7e
1777d0e6c549c6c105a2d9f03a0ca8076b42299b9da111e3e8efc5a02329959804e070e868b2726
3b7b5607d799553709fe4bb9d0d07ce13b548b01dedf9afd033dda8e4a81639123160e7eb115cc0
d16cb83c42b91affb029f846b07d86ec79b6b2895b337f01d12b3")]

NB: The key is split onto several lines for formatting purposes only. In your code, this must appear on a single line.

The public key can be obtained using the strong name tool in a two-stage process. Firstly, the public key must be extracted from the key file into a new file. This is achieved using the "p" switch. For example, if the key file is named "Caller.snk", you can copy the public key to a new file named "public.key" from the Visual Studio command line using the following command:

sn.exe -p Caller.snk public.key

The second stage is to view the contents of the new public key file in hexadecimal format. This can be outputted directly using the following command. This key value can then be copied and pasted into the assembly attribute.

sn.exe -tp public.key
Link to this Page24 November 2008
Twitter RSS Feed