3

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?

GregRos
  • 8,667
  • 3
  • 37
  • 63
  • Interesting catch - will check it myself. – Jon Skeet Nov 30 '13 at 21:15
  • Hmm - it works fine for me. What compiler version are you using? – Jon Skeet Nov 30 '13 at 21:17
  • @JonSkeet I see the same issue and I'm using VS 2012, i.e. C# compiler version 4.0.30319.17929. – Mike Zboray Nov 30 '13 at 21:25
  • @mikez: Maybe it was fixed in 4.0.30319.33440, which comes with VS 2013? – Jon Skeet Nov 30 '13 at 21:26
  • Hmm. Works with me for v3.5 as well though. Will post my code as an answer... – Jon Skeet Nov 30 '13 at 21:27
  • I'm also using VS 2012 with csc v4.0.30319.17929. – GregRos Nov 30 '13 at 21:33
  • @GregRos: Can you try reproducing with the exact files I've given in my answer? If those work for you, then you'll need to update your question with a more representative bit of code. – Jon Skeet Nov 30 '13 at 21:40
  • Aha - doesn't work for me when specifying the assembly name with `/out`. Right, this looks like it's effectively a matter of going back to the "normal" behaviour of not being able to change access... – Jon Skeet Nov 30 '13 at 21:44
  • Just curious, how many overrides do you have? One option would be to make a non-virtual internal method that simply calls a protected virtual method. – Mike Zboray Nov 30 '13 at 21:47
  • I have very many of them. Too many to do that sort of thing. I'd rather just move the shared class to a different assembly and not use the attribute, though I'd prefer a different solution; this will introduce other problems, such as involving cyclical references. – GregRos Nov 30 '13 at 21:51

1 Answers1

4

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?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Yes, I understand. I guess, considering it in this way, it couldn't really have worked in any other way. Increasing accessibility seems to be better than reducing it in this case, since the latter leads to inconsistent results. The inconvenience I am experiencing is more due to how C# requires accessibility modifiers to be specified explicitly, rather than to the the action of the attribute, which actually makes sense. – GregRos Dec 03 '13 at 13:05