Reader Level:

C# Friendly Assemblies: Looking at Building Truly Reusable Components

By Matthew Cochran on Nov 01, 2006
This article discusses how we can hide our base class implementation and require referencing of our objects through their interfaces. This keeps our own projects loosely coupled and also allows us to publish our assemblies as truly reusable components because they are 100% “Black box”.


We will accomplish building reusability into our project by exposing the interfaces to our objects publicly while hiding the implementation in an assembly containing only internal classes and then exposing the interfaces to our classes in a "builder" assembly.  The builder assembly will be the only assembly allowed to make instances of the core objects so the instantiation of our objects are effectively hidden and not available outside the builder classes.

Project Core

For this project we will build a simple Money object that can be added. Our four assemblies will consist of

  1. FriendlyTesting.Core: Contains our interface definitions.
  2. FriendlyTesting.Hidden: Contains our implementation in classes internal to this assembly.
  3. FriendlyTesting.Friend: Exposes instantiation of the internal classes in FriendlyTesting.Hidden.
  4. FriendlyTesting.TestHarness: The final test harness console project.



Our interfaces define an IMoney interface that is IAddable<IMoney> (so we can add up our money in the test harness).


Concrete Implementation

The internal Money class implements IMoney and is "hidden" because it can only be instantiated from inside the containing assembly or a friend assembly.



The builder class will implement an interface specific to the builder: IMoneyMaker. The builder's responsibility is to instantiate the Money object and expose it through the IMoney interface. 


Our solution

The first thing to note is that the Money is hidden in it's assembly because it is marked as internal.

internal class Money: IMoney, IAddable<IMoney>

As a result, from our money builder class, the money is not available because the Money class is only internally available to the FriendlyTesting.Hidden assembly.  As a result, we can not instantiate a Money object until we make the FriendlyTesting.Friend assembly a "friend" of the FriendlyTesting.Hidden assembly indicating that it can be trusted with the internal classes.

public class MoneyBuilder: IMoneyMaker



    #region IMoneyMaker Members


    public IMoney MakeMoney(double amount)


        // Money is not available here...

        // it is internal to FriendlyTesting.Hidden





Creating a Friendly Assembly

Our goal is to make instantiation possible only through our builder class, so in the next few steps we'll make these two assemblies play nicely with each other.

Step 1) Strongly Naming the Assemblies

The first step is to make all the assemblies strong named.  So we will create keys for our two assemblies (friend and hidden) through the "sn"  visual studio command line utility (Start>Programs>Visual Studio>Visual Studio Tools>Visual Studio Command Prompt).

Command line syntax:

sn -k <new key name>

Actual command:

sn -k FriendlyKey.snk

Generating the key for the FriendlyTesting.Friend assembly:

Generating the key for the FriendlyTesting.Hidden assembly:


Because these two assemblies are strongly named, all referenced assemblies also have to be strongly named, so we'll do the same thing for the FriendlyTesing.Core assembly that holds our core interface definitions.

Step 2) Add the Keys to the Projects

Now we have to add the keys we generated to their respective projects through the project's properties (right click on the project and select 'Properties').  We'll walk through this for the FriendlyTesting.Friend assembly:

Click "Sign the assembly" and select browse from the drop down that is activated.

Open FriendKey.snk and it will now appear in our project


Add HiddenKey.snk to the friendlyTesting.Hidden project and the core key to the core project in the same way.


Step 3) Extract public key for friend assembly

Next we will extract the public key from our FriendlyKey.snk (the key for the FriendlyTesting.Friend project).  We need information from the public key in order to let the hidden project know which assemblies to be friendly with.  We will generate a new public key called "FriendlyKey_public.snk" with the following command:

Command line syntax:

sn -p <existing private key name> <new public key to be created>

Actual command:

sn -p FriendlyKey.snk FriendlyKey_public.snk

Step 4) find out the public key's info

Next we will show the public key's info with the following command

Command line syntax:

sn -tp <public key name>

Actual command:

sn -tp FriendlyKey_public.snk


Step 5) Name a friend in the hidden assembly.

Next, we will update the Assembly.cs file in the FriendlyTesting.Hidden project making our hidden assembly's internal member available to the new friend.

Add the following line which contains the public key information we just gathered to the FriendlyTesting.Hidden Assembly.cs file.  Assembly.cs is located in the project's Properties folder:

[assembly: InternalsVisibleTo("FriendlyTesting.Friend, PublicKey=00240000048000009400000006020000002400005253413100040000010

After this we find that FriendlyMoney.Friend has access to the internal members of the FriendlyTesting.Hidden project.  So we can make money with our MoneyBuilder.

public class MoneyBuilder: IMoneyMaker


    #region IMoneyMaker Members


    public IMoney MakeMoney(double amount)


        return FriendlyTesting.Hidden.Money.Instantiate(amount);





Wrap up: Test Harness

In any other projects in which we would like to use our Money class, we can only instantiate it through the MoneyBuilder and our Money object can now only be referenced through the IMoney or IAddable<> interfaces. 

class Program


    static void Main(string[] args)


        IMoneyMaker builder = MoneyBuilder.Instantiate();


        // NOTE: FriendlyTesting.Hidden has no members available here

        // so we are required to get IMoney through the MoneyBuilder


        IMoney bagODoughA = builder.MakeMoney(10.25);

        IMoney bagODoughB = builder.MakeMoney(5.50);

        IMoney allDough = bagODoughA.Add(bagODoughB);


        Console.WriteLine("Total Moolah: " +







This approach gives us a great deal of flexibility because we control how our class will be consumed by other projects and we can ensure nothing will break by changing backend implementation... as long as the code works and the IMoney and IAddable<> interfaces do not change.


If you download the project, you can verify that the Money object is not available to the FriendlyTesting.TestHarness assembly. Also, if you remove the line added to FriendlyTesting.Hidden Assembly.cs, the project will not compile because the Money object will no longer be visible to the FriendlyTesting.Friendly assembly.

I hope you found this article useful.

Until next time,
Happy coding 

Matthew Cochran
Matthew Cochran

Software Architect, Team Lead, Engineer. Twitter: @Matt_Cochran



JetBrains ReSharper Ultimate
.NET code analysis, refactorings, navigation, unit testing, code coverage and profiling in Visual Studio, a bonus for C++ devs, bundled at 50% off.

Trending up