Knights, Knaves, Protected and Internal

Knights, Knaves, Protected and Internal

Rate This
  • Comments 42

Knight When you override a virtual method in C# you are required to ensure that the stated accessibility of the overridden method - that is, whether it is public, internal, protected or protected internal(*) – is exactly re-stated in the overriding method. Except in one case. I refer you to section 10.6.4 of the specification, which states:

an override declaration cannot change the accessibility of the virtual method. However, if the overridden base method is protected internal and it is declared in a different assembly than the assembly containing the override method then the override method’s declared accessibility must be protected.

What the heck is up with that? Surely if an overridden method is protected internal then it only makes sense that the overriding method should be exactly the same: protected internal.

I’ll explain why we have this rule, but first, a brief digression.

A certain island is inhabited by only knights and knaves. Knights make only true statements and only answer questions truthfully; knaves make only false statements and only answer questions untruthfully. If you walk up to an inhabitant of the (aptly-named) Island of Knights and Knaves you can rapidly ascertain whether a particular individual is a knight or a knave by asking a question you know the answer to. For example “does two plus two equal four?” A knight will answer “yes” (**), and a knave will answer “no”. Knaves are prone to saying things like “my mother is a male knight”, which is plainly false.

It might seem at first glance that there is no statement which could be made by both a knight and a knave. Since knights tell the truth and knaves lie, they cannot both make the same statement, right? But in fact there are many statements that can be made by both. Can you think of one?

.

.

.

.

.

.

.

Both a knight and a knave can say “I am a knight.”

How does that work? The reason this works is because the pronoun “I” refers to different people when uttered by different people. If Alice, a knight, makes the statement “I am a knight”, she is asserting the truth that “Alice is a knight”. If Bob, a knave, makes the statement “I am a knight”, he is not asserting the true statement “Alice is a knight” but rather the false statement “Bob is a knight”. Similarly, both Alice and Bob can assert the statement "My name is Alice," for the same reason.

And that’s why overriding methods in a different assembly aren’t “protected internal”. The modifier “internal” is like a pronoun; it refers to the current assembly. When used in two different assemblies it means different things. The purpose of the rule is to ensure that a derived class does not make the accessibility domain of the virtual member any larger or smaller.

An analogy might help. Suppose a protected resource is a car, an assembly is a dwelling, a person is a class and descendent is a derived class.

  • Alice has a Mazda and lives in House with her good friend Charlie.
  • Charlie has a child, Diana, who lives in Apartment.
  • Alice has a child, Elroy, who lives in Condo with his good friend Greg.
  • Elroy has a child – Alice’s grandchild -- Frank, who lives in Yurt.

Alice grants access to Mazda to anyone living in House and any descendent of Alice. The people who can access Mazda are Alice, Charlie, Elroy, and Frank.

Diana does not get access to Mazda because she is not Alice, not a descendent of Alice, and not a resident of House. That she is a child of Alice’s housemate is irrelevant.

Greg does not get access to Mazda for the same reason: he is not Alice, not a descendent of Alice, and not a resident of House. That he is a housemate of a descendent of Alice is irrelevant. 

Now we come to the crux of the matter. Elroy is not allowed to extend his access to Mazda to Greg. Alice owns that Mazda and she said "myself, my descendents and my housemates". Her children don't have the right to extend the accessibility of Mazda beyond what she initially set up. Nor may Elroy deny access to Frank; as a descendent of Alice, Frank has a right to borrow the car and Greg cannot stop him by making it "private".

When Elroy describes what access he has to Mazda he is only allowed to say "I grant access to this to myself and my descendents" because that is what Alice already allowed. He cannot say "I grant access to Mazda to myself, my descendents and to the other residents of Condo".

-----------------

(*) Private virtual methods are illegal in C#, which irks me to no end. I would totally use that feature if we had it.

(**) Assuming that they answer at all, of course; there could be mute knights and knaves.

  • So "private virtual" methods are only needed by developers who work with such large assemblies that they can't trust developers writing code in the same assembly to respect their invariants? That doesn't seem like a terribly good reason to add a feature to a language.

  • They could perhaps both tell you that Schrödinger's cat-in-a-box is dead (or, for that matter, alive). Of course, the knight may also choose to strike you down for your unnecessary animal testing / cruelty, which may be a give-away. Of course, Pratchett understands the real truth of this matter: http://www.discworldmonthly.co.uk/tpquote.php?qn=284&mode=goto

  • @Gabe - Designing so that the computer enforces the devlopers intentions is always a good thing. It is not even a matter of "trusting others"; it also lowers the chances that a single developer will make a mistake [we ae all human].

  • So, what is the purpose of having a method marked as "protected internal"? From what I see, "protected" basically nullifies the "internal" part, since any class in other assemblies can inherit me and then call base.MyMethod(). Or am I missing something?

    The other thing that I find a bit strange (not wrong, but strange at first glance), is that the same base internal method, in the same superclass, can be called as base.MyMethod(), but not as objectInstance.MyMethod()

    namespace Project1

    {

       public class Foo

       {

           internal protected virtual string Baz()

           {

               return "Foo.Baz";

           }

       }

    }

    namespace Project2

    {

       public class Bar : Foo

       {

           public string StealBaz1()

           {

               Foo foo = new Foo();

               return foo.Baz(); // illegal

           }

           public string StealBaz2()

           {

               return base.Baz(); // legal

           }

       }

    }

  • @gabe

    "So "private virtual" methods are only needed by developers who work with such large assemblies that they can't trust developers writing code in the same assembly to respect their invariants? That doesn't seem like a terribly good reason to add a feature to a language."

    Then why do we have private at all? We could all get by perfectly with internal...after all we trust our developers don't we?

  • Pavel wrote: <quote>There is an old adage regarding "protected" modifier, which goes as follows: "Remember that 'protected' really means 'public'!". And that is very true. Sure, you only expose the member to derived types - but they can expose it to anyone they want!</quote>

    This couldn't be more wrong.  Derived classes can give away access only to protected members of instances of the derived class.  Derived classes get exactly zero access to protected members of instances of other descendants of the base class.  And since C# actually enforces the runtime type cast, you can't fool the compiler into giving you access you shouldn't have.

  • @Ben Voigt

    I think what Pavel was implying is that when you design an extensible class, marking a method as protected does not ensure that you are limiting access to a derived class:

    Class A

    {

      protected void SomeMethod() {}

    }

    Class B: A

    {

      public void SomeOtherMethod() { SomeMethod() }

    }

    So essentially anyone consuming B can have public access to a theoretically protected member of A even if it was meant to be protected. Horrible practice but possible and thus Pavel's adage.

  • Pavel Minaev, Skynyrd, etc.:

    Sure, a derived class can wrap a protected or protected-internal method and provide public access, but in Eric's analogy, Elroy can still give the Mazda to Greg - it's just that he's not ALLOWED to.  Nothing is *physically* preventing him from doing it, but when he does it, he's being a bad boy.

    Same with derived class.  If a base class hands you a protected method and you provide a public wrapper around it, you're being very naughty, and there's a good chance that you might be inadvertently (or intentionally) breaking something or introducing a security hole.

  • @Aaron G

    I agree completely...I'm not saying it should be done, I'm just stating the fact that it can be done in response to Ben's post and clarifying what I think Pavel meant to say with his adage.

  • David V. Corbin: If you're just worried about accidentally using a method that was intended to be "private virtual", you can use a naming convention. If you decide that all such methods should start with "vp_" then you can easily scan your code for "foo.vp_..." and see that you're using a method of foo that you're not supposed to.

    Focus: You're right; we don't really need private because internal would do. In fact many languages just use naming conventions to provide data hiding. Python programmers just know that any member starting with an underscore is not part of an object's public interface, for example, and it doesn't seem to be a problem.

    In the case of C#, of course, you don't want Intellisense to show you every possible member of every variable -- you want it to only show you the ones that you're expected to use. So I would never advocate not using private even though technically it may not be necessary.

  • @Gabe

    Ok I could agree with that, but that is an alltogether different argument.

    Obviously C# and Python are two completely different approaches. Private virtual methods in Python would not make any sense, but in C# where we DO use private methods, fields, etc., private virtual does make sense and it fits with the "black box" object oriented approach of the language.

    Personally I prefer 100 times more access modifiers than funky names. Funky names are after all just another way to enforce accessibilty rules so its obvious that some kind of access restrictions, even between trusted coders of the same team are always necessary in any programming language. Instead of using names that mean "hey! dont mess with this method, its only intended for internal use", why not let the language itself enforce it?

  • "Elroy can still give the Mazda to Greg - it's just that he's not ALLOWED to.  Nothing is *physically* preventing him from doing it, but when he does it, he's being a bad boy."

    But the "not ALLOWED to" is part of the specific analogy, which is questionable as to whether it's applicable to all possible situations in C#. The inability to widen access in derived classes is a (poor) attempt to physically prevent it. And it only even reflects the intent of the author of the base class _because_ of the existence of this restriction - if the restriction did not exist, then there would be no reason to think that the developer of the base class did not intend for it to be possible to widen access.

Page 3 of 3 (42 items) 123