Search code examples
c#inheritanceassembliesinternalsvisibleto

Issue with InternalsVisibleTo attribute


I have two assemblies, say Main and Sub, where Sub depends on Main. Main defines a few classes that have protected internal virtual members, that I want to override in Sub. I override these members as protected override.

There is an unrelated class in Main, call it Main.Shared, that I want to use in Sub, but I don't want any other assemblies to see it. Here is how the situation looks:

//In assembly Main:
public class Shared
{
}
public class Parent
{
    protected internal virtual void DoStuff()
    {

    }
}
//In assembly Sub:
public class Child : Parent
{
    protected override void DoStuff()
    {
        base.DoStuff();
    }
} 

So I used the InternalsVisibleTo attribute as usual. However, after I decorate Main with this attribute, the code refuses to compile. The error message says that I must now override DoStuff as protected internal override, presumably because it now thinks Main and Sub are the same assembly (?)

This is a big problem, since it means that I need to manually change every single override to protected internal, and there are many of them. Moreover, I might want to remove the attribute later, and then I would need to change everything back again.

Is there any way I can avoid doing this? (Besides a complete redesign of the code base...)

I'm also curious about why this happens at all. Is this behavior just some sort of blind spot, or is it supposed to work like this?


Solution

  • Okay, I think I now understand it. While the C# spec doesn't call this out, it doesn't actually mention InternalsVisibleTo at all. I think the way to understand it is that you can't change the acceptable set of call sites for a member by overriding it.

    For all other accessibility modifiers, that just means you've got to stick to the same modifier - but protected internal is slightly different. Without InternalsVisibleTo, that's accessible within the original assembly and within subclasses (subject to the normal rules of protected, which are slightly hard to write accurately but succinctly). When you override that in a different assembly, you normally have to make it protected so that it's still only available to subclasses and to the original assembly - rather than to your "new" assembly.

    But now when you bring InternalsVisibleTo into the picture, making it protected would reduce the accessibility - because all code within the second assembly already has access to the member. So you need to keep it as protected internal to preserve that. (The second assembly doesn't have to have its internals visible to the original one, because the original one can't refer to the second one anyway, as then you'd have a circular reference.)

    It still falls down though - because that might still increase accessibility - if you have assembly A whose internals are visible to assembly B, and assembly B whose internals are visible to assembly C, then a protected internal method in assembly A should be visible to both assemblies A and B; but when you override it in assembly C you can only make it visible to assemblies B and C, or just to subclasses. At this point you really want to make it visible to "assembly A and the assemblies it trusts" - but there's no way of expressing that.

    Make sense?