When developing libraries it occasionally becomes necessary to expose a different public interface to different groups of users. The most common scenario is one in which your library needs to be accessed in one way by applications that use it, but another way by other libraries that extend it. You want extension developers to have access to all the behind-the-scenes details, but exposing those properties and methods to applications would be confusing or even damaging by promoting improper use. In other words, you want the internal
properties and methods to be exposed to one set of developers but not another. In this post I'll examine a strategy for exposing different public APIs to different sets of users.
One way of accomplishing this is to make the extensions friend assemblies by using the InternalsVisibleTo
attribute. While tempting, this is a bad idea. Friend assemblies work well for unit testing scenarios and for some enterprise-style development where there is a tight known coupling between components and all the code is well controlled. In any other case, they prevent proper extension by requiring the base library to know about all the extension libraries at compile-time. The web is strewn with disillusioned developers sharing anecdotes of how architectures that rely on InternalsVisibleTo
caused them pain and agony.
So if we don't want to make our internal
members public
for everyone and we also don't want to expose them to specific libraries with InternalsVisibleTo
, what can we do? The answer is extension methods. One interesting trait of extension methods is that they aren't available unless their namespace is in scope. This allows us to create sets of extension methods that are only visible if certain namespaces have been explicitly imported.
Consider the following class:
namespace MyLibrary { public class Car { public int NumberOfTires { get; internal set; } } }
Assume that the number of tires is set by some sort of car factory class internal to the library and we don't want normal library users to change it. However, let's say we did want extension developers to have access to the tire count so that they could create alternate factory classes. Using this approach, the answer would be to create an extension method that would allow changing the number of tires in a special namespace:
namespace MyLibrary.Internal { public static class CarExtensions { public static void SetNumberOfTires(this Car car, int numberOfTires) { car.NumberOfTires = numberOfTires; } } }
Because the SetNumberOfTires()
extension method is still in the MyLibrary
project, it has access to the internal
NumberOfTires
setter. In essence, the extension method is proxying the internal
property and making it public
. All an extension library has to do in order to use it is to add using MyLibrary.Internal;
to any code that needs access.
There are a couple drawbacks to this approach. The first is that by exposing internal
code through public
extension methods, those bits aren't actually hidden from outside use anymore. While segregating the extensions into a special namespace makes sure they won't pollute the public API, this strategy shouldn't be used if you truly want those properties or methods to remain unavailable to outside code. Another drawback is that the API used to access the internal
code doesn't directly match the internal
code. For example, you'll end up with a lot of .GetXyz()
and .SetXyz()
extensions since you can't create extension properties. Also, you obviously can't expose entire classes this way (though I suppose you could put interfaces or proxy classes in the internal namespace for this purpose). Finally, it requires duplicating portions of your code. For every internal
property or method you want to expose, you also have to write and maintain a matching extension method. However, if you can live with these limitations and feel that a clean API for different sets of consumers is more important than the maintenance burden, this might just do the trick.