russia is waging a genocidal war in Ukraine. Please help Ukraine defend itself before russia has a chance to invade other countries.
Exploring the Intersection of Software Development, AI Innovation, and Entrepreneurial Success | Friend assemblies in .NET

Friend assemblies in .NET

What is friend assemblies in .NET and how it works? Friend assembly is an assembly that can access another assembly's or project Friend (Visual Basic) or internal (C#) types and members. If you identify an assembly as a friend assembly, you no longer need to have this types and members as public in order for them to be accessed by other assemblies. Why you need friend assembly especially if you senior developer?

This is convenient in the following rules:

  1. During unit testing, when test code runs in a another assembly but requires access to members in the assembly being tested that are marked as Friend VB or internal C#.
  2. When you are developing a class library and additions to the library are contained in separate assemblies but require access to members in existing assemblies that are marked as Friend (Visual Basic) or internal (C#).
 

You can use the InternalsVisibleToAttribute attribute to identify one or more friend assemblies for a given assembly. The following example uses the InternalsVisibleToAttribute attribute in assembly A and specifies assembly AssemblyB as a friend assembly. This gives assembly AssemblyB access to all types and members in assembly A that are marked as Friend (Visual Basic) or internal (C#).

When you compile an assembly (assembly AssemblyB) that will access internal types or internal members of another assembly (assembly A), you must explicitly specify the name of the output file (.exe or .dll) by using the /out compiler option. This is required because the compiler has not yet generated the name for the assembly it is building at the time it is binding to external references. For more information, see /out (C#) and /out (Visual Basic).

Only assemblies that you explicitly specify as friends can access Friend (Visual Basic) or internal (C#) types and members. For example, if assembly B is a friend of assembly A and assembly C references assembly B, C does not have access to Friend (Visual Basic) or internal (C#) types in A.

The compiler performs some basic validation of the friend assembly name passed to the InternalsVisibleToAttribute attribute. If assembly A declares B as a friend assembly, the validation rules are as follows:

  1. If assembly A is strong named, assembly B must also be strong named. The friend assembly name that is passed to the attribute must consist of the assembly name and the public key of the strong-name key that is used to sign assembly B. The friend assembly name that is passed to the InternalsVisibleToAttribute attribute cannot be the strong name of assembly B: do not include the assembly version, culture, architecture, or public key token.

  2. If assembly A is not strong named, the friend assembly name should consist of only the assembly name.

  3. If assembly B is strong named, you must specify the strong-name key for assembly B by using the project setting or the command-line /keyfile compiler option.

The StrongNameIdentityPermission class also provides the ability to share types, with the following differences:

  1. StrongNameIdentityPermission applies to an individual type, while a friend assembly applies to the whole assembly.

  2. If there are hundreds of types in assembly A that you want to share with assembly B, you have to add StrongNameIdentityPermission to all of them. If you use a friend assembly, you only need to declare the friend relationship once.

  3. If you use StrongNameIdentityPermission, the types you want to share have to be declared as public. If you use a friend assembly, the shared types are declared as Friend (Visual Basic) or internal (C#).

For information about how to access an assembly's Friend (Visual Basic) or internal (C#) types and methods from a module file (a file with the .netmodule extension), see /moduleassemblyname (Visual Basic) and /moduleassemblyname (C#).

using System.Runtime.CompilerServices;
using System;

[assembly: InternalsVisibleTo("AssemblyB")]

// The class is internal by default.
class FriendClass
{
public void Test()
{
Console.WriteLine("Sample Class");
}
}

// Public class that has an internal method.
public class ClassWithFriendMethod
{
internal void Test()
{
Console.WriteLine("Sample Method");
}

}

Type visibility vs member accessibility

One of the things I’ve noticed while trying to help others get started with the .NET framework is that they tend to confuse type visibility with member accessibility. In this quick post I’ll try to point out the differences between these two concepts. Let’s start with type visibility. When you create a type (ex.: class, struct, etc.) it may be visible to all the code (public keyword) or only to the types defined in the same assembly as that type (internal keyword). In C#, you can use the public or the internal qualifier to define the visibility of a type. By default, all the types which haven’t been explicitly qualified with one of these keywords is considered to be internal:

//internal by default
struct T {
//…
}

Member accessibility is all about specifying the visibility of the members of a type. In other words, the accessibility indicates which members might be accessed from some piece of code. Currently, the C# allows you do use 5 of the 6 supported CLR member accessibility options:

  1. private: members qualified with the private keyword (C#) are only accessible by other members defined in the same type or in a nested type.

  2. family: members qualified with the protected keyword (C#) can only be accessed by methods in the defining type, nested type or one of its derived types.

  3. family and assembly: you *can’t* use this accessibility in C#. This accessibility says that a member can only be used by methods in the same type, in any nested type or in any derived type defined in the *same* assembly as the current type.

  4. assembly: in C#, you use the internal keyword to specify this accessibility level. In this level, the member can only be accessed by all the types defined in the same assembly as the current type.

  5. family or assembly: in C#, you need two keywords to specify this level: protected internal. In practice, it means that the member is accessible by any member of the type, any nested type, any derived type (*regardless* of the assembly) or any other method in the same assembly as the current type.

  6. public: members qualified with the public keyword (C#) can be used by any other member in any assembly.

Before going on, it’s important to notice that member accessibility depends always in the visibility of the type. For instance, public members exposed by an internal type in assembly A *cannot* be used from assembly B (by default) since the type isn’t visible in that assembly. In C#, if you don’t specify the accessibility of a member, the compiler will default to private in most cases (one exception: interface methods are always defined as public!). In C#, when you override a member in a derived type, you must use the same accessibility as defined in the base class. Interestingly, this is a C# restriction since the CLR does allow you to change the accessibility of a member in a derived class to a less restrictive level (ex.: you can go from private to protected in the override, but not the other way around). There’s still a couple of things I have to say about member accessibility, but I’ll leave it for a future post. Stay tuned for more.

Hexademical key

What is the quickest way to get the key in hexadecimal? If you’re thinking reflector, you’re right! Just change the language to IL and you’ll get your key in hex:

 

Friend assemblies in .NET

Friend assemblies description

A type can be visible to all the other types defined in the same assembly (internal) or it can be visible to any type, independently from the assembly where it’s defined. On the other hand, member accessibility controls the exposure of a member defined by a type. By default, internal types cannot be instantiated from types defined in other assemblies. That’s why you’ll typically define your helper types as internal so that they can’t be consumed by types defined in other assemblies. There are, however, some scenarios where you’d like to grant access to all the types defined in another assembly and, at the same time, block access to all the other types defined in other assemblies. That’s where friend assemblies can help. When you create an assembly, you can indicate its “friends” by using the InternalsVisibleToAttribute. This attribute expects a string which identifies the assembly’s name and public key (interestingly, you must pass the full key – not a hash – and you’re not supposed to pass the values of the culture, version and processor architecture which you’d normally use in a string that identifies an assembly). Here’s a quick example which considers the LA.Helpers assembly a friend:

[assembly:InternalsVisibleTo(“LA.Helpers, PublicKey=12312…ef”)]

As I’ve said before, you *do* need to pass the full public key (if the assembly isn’t strongly signed, then you just need to pass its name). In practice, all the types defines in the LA.Helpers assembly can now access all internal types defined on the assembly which contains the previous instruction. Besides getting access to all the internal types, friend assemblies can also access all of internal members of any type maintained in that assembly.

You should probably think carefully about the accessibility of your type’s members when you start grating friend access to other assemblies. Notice also that creating a friend relationship between two assemblies ends up creating a high dependency between  them and that’s why many defend that you should only use this feature with assemblies that ship on the same schedule. Notice that in my experience, I’ve ended up using this feature *only* for testing helpers that I don’t want to expose publicly from an assembly.

I think that most of us don’t use the command line for building any projects, but this post wouldn’t really be complete without mentioning some interesting details about the C# compilation process. When you’re building the friend assembly, you should use the /out parameter and pass the name of the assembly. This should improve your compilation experience since because you’re giving the compiler what it needs to know to check if the types defined in the assembly can access the internal types of the other assemblies. If you’re compiling a module (is anyone doing this in a regular basis?), then don’t forget to use the /moduleassemblyname parameter to specify the name of the assembly that will contain the module (the reason is the same as the one presented for the /out parameter).

Summary

In this article we discussed friend assemblies in .NET Framework feature There are similarities between the InternalsVisibleToAttribute attribute and the StrongNameIdentityPermission class. The main difference is that StrongNameIdentityPermission can demand security permissions to run a particular section of code, whereas the InternalsVisibleToAttribute attribute controls the visibility of Friend (Visual Basic) or internal (C#) types and members.

Friend assemblies in .NET

Comments are closed