Why C# does not support the intersection of Protec

2019-04-18 08:50发布

问题:

protected internal:

The union of protected and internal accessibility (this is less restrictive than protected or internal alone)

The CLR has the concept of intersection of protected and internal accessibility, but C# does not support this.

So my question is:

What's the meaning of omitting this Access Modifier, is there a concrete reason? So why C# should not support it?

回答1:

Update: C#7.2 is introducing this with the access modifier private protected, which seems wrong in a few ways but does avoid much of the potential for confusion I describe below, so is perhaps the best of a bad bunch.

Personally, I've wanted this quite a few times. There are times when one exposes a class and one or more classes derived from it as public in an assembly, and there are times when some member of the base class is only used by those derived classes and should not be exposed to protected classes in other assemblies (quite often the constructor, so as to prevent other assemblies from having classes that derive from it at all).

It is of course always good to define your access as restrictively as possible, and so the intersection of protected and internal is precisely what is wanted here.

Instead, I've had to kludge it by declaring the member internal. There is now a potential for bugs in my code that wouldn't have been there if I used a language that allowed me to use that intersection.

However, consider the downside.

As it is, there's some confusion about the way that protected internal gives the union of protected and internal. It's probably the most misunderstood access, judged by questions on sites like this.

What should we call it? internal protected? Can you imagine how often people would get that confused with protected internal? We'd want something more clearly differentiated, and we'd want the same for internal protected (because we've still increased its potential for confusion). It's not an impossible problem to answer, but keeping the number of keywords down is a good idea too.

Even if a perfect answer is found to the naming question, the potential for confusion by introducing yet another level of access is not entirely defeated.

So with that in mind, let's look at the upside again. We no longer have to kludge the times we need it by using internal, reducing bugs caused by inappropriately using such a member. Okay, how often does that come up, and how likely would such bugs actually be? Not very often really, and not very likely.

On balance therefore, while I do find myself occassionally wishing C# had this, a moment's pause normally makes me glad they did not.



回答2:

The intersection of protected and internal would downgrade to a simple internal for code external to your library – meaning it would not accessible, whether from derived classes or otherwise.

As such, the only benefit a protected internal intersection would serve is for disciplining yourself, as the developer of your library, not to access the type/member except from the same or derived classes within your library. It will not make any difference for consumers of your library, since its semantics would be identical to a plain internal.

That said, I would not have found it unreasonable for it to be allowed in C#. Self-discipline often comes in useful; it is the reason why the language designers differentiated between private and internal, despite the two being identical for external consumers.

However, since the distinction between the intersection and the plain internal is small, they probably chose to exclude it in order to mitigate the risk of developers confusing it with the more-useful protected internal union.



回答3:

It was a design decision, but consider what it means:

  • accessible only to derived classes in the same assembly.

This is not a very useful boundary, I can't think of a clear use-case.
And that's why it probably wasn't worth coming up with a new keyword(-combination).

I would rather ask why the CLR supports it. Orthogonality I suppose.



回答4:

So why C# should not support it?

Wrong question. The question should be "why should C# support protected and internal?"

Keep in mind, the natural state of a feature is for it to not be implemented. Implementing a feature is very expensive in terms of resources, testing, and, let's not forget, opportunity cost (meaning it displaces other features). So to overcome this expense, there had better be an extremely clear benefit for implementing a feature that overcomes these costs.

In particular, what is the value of protected and internal? There really is no good reason to restrict accessibility only to derived classes in the same assembly.

IMO it's odd the CLR supports it, but it does. That doesn't mean that C# needs to though.



回答5:

I wanted to ask this same question, but thankfully SO found this one for me.

I can understand the point of view that it would require either at least a new keyword, or new semantics for existing keywords, so it wasn't supported because the cost-benefit didn't work in its favour.

However, I question whether it's legitimate to avoid implementing this CLR feature because of the narrow use-case or because it could confuse people.

One argument given above is that it's only use would be to enforce self-discipline.

But then the same could be said of any visibility modifier that isn't public. The idea, also, that it would only apply to 'a single developer of a single library' is plainly erroneous, too. Many libraries are developed by teams of people, and it's entirely natural for a developer within that team to add a member that he or she would want to restrict only to internal types derived from his or her base.

Then there's this scenario:

internal class Foo {

}

public class PublicBase {
  protected Foo MyFoo { get; set; }
}

In the case here, I have an internal type that I want to expose on a class, but I only want to expose it to derived types. Since the type is internal, that implicitly means derived types in the same assembly. External types can still legitimately inherit from this type, but they won't need to use this member.

In this situation I'm stuck: I can't use protected, nor can I use protected internal.

Instead, I have to use internal, which incorrectly broadcasts to other members of my team that it should be used from code outside the class. Sure I can stick a comment on it (and even stick the EditorBrowsableAttribute of Never on it, but that would hide it from internal inheritors too) - but it's not the same.

As it is, the only way I can achieve this is to hack the assembly with an IL rewrite after the build - but I need this visibility (or lack of it) at compile-time, not after it's built.

Ultimately, arguments about "what keyword(s) would it use" or "how would it be implemented" are irrelevant to me: I'm the programmer, not the C# language designer.

Equally, I have to say arguments like "Well, protected internal is confusing enough for many" are also irrelevant. There are so many developers I've come into contact with, either in person or virtually, who don't understand things like ?: or generic constraints - guess what, they don't use them! That doesn't mean I shouldn't.

So yes I understand the reasons for not implementing it - but I disagree with them - therefore my answer for why C# doesn't support it is because the C# designers got it wrong.

And yes I'm ready for the heat that might bring to my door :)



回答6:

You can always do:

public MyExternallySealedClass
{
     internal MyExternallySealedClass() {...}

     ...
}

As long as you do not let any external consumer instantiate the object what would you care if they try deriving it? Short answer, the feature you request brings nothing new and you already have tools to do what you are requesting.



回答7:

As far as I know you can use intersection of protected and internal access modifiers but not with the interpretation you want (maybe because of what Henk said):

  1. Protected: The type or member can only be accessed by code in the same class or struct, or in a derived class.

  2. Internal: The type or member can be accessed by any code in the same assembly, but not from another assembly.

  3. Protected Internal: The type or member can be accessed by any code in the same assembly, or by any derived class in another assembly.