Why Can’t I Access A Protected Member From A Derived Class, Part Two: Why Can I?


This is a follow-up to my 2005 post on the same subject  which I believe sets a personal record for the longest time between parts of a series. (Of course, I didn’t know it was a series when I started it.) Please read the previous article in this series, as this post assumes knowledge of part one.

…….

OK, now that you’ve read that, it’s clear why you can only access a protected member from an instance of an object known to be of a type at least as derived as the current context. You can therefore deduce the answer to the question asked to me by a (very polite) reader this morning: Why did this code compile in C# 2.0 but give an error in C# 3.0?

public abstract class Item{
  private Item _parent;
  public Item Parent {
    get { return _parent; }
    protected set { _parent = value; }
  }
}
public class Bag:Item{
  private List<Item> list = new List<Item>();
  public void Add(Item item)
  {
    item.Parent = this; // Error in C# 3.0
    list.Add(item); 
  }
}
public class Torch : Item { }

It compiled in C# 2.0 because the compiler had a bug. We forgot to enforce the semantics of “protected” access for the property setter. Though the compiler did not generate an error, it did generate code which would not pass the CLR verification check! The code would run if it happened to be fully trusted, but it was not safe to do so. We fixed the bug in C# 3.0 and took the breaking change.

This raises a more interesting point though. Now that we correctly prevent you from setting the “parent” reference in this manner, how would you implement the desired pattern? The reader wanted the following conditions to be met:

  • There are two kinds of items — “leaf” items, and “container” items. Container items may contain either kind of item, so you could have a box containing a bag, which in turn contains a torch.
  • An item may be in a container, and if it is, its parent reference refers to that container.
  • These classes must be extensible by arbitrary third parties.
  • “Unauthorized” code must not be able to muck around with the parenting invariants. 

The reader was attempting to enforce these invariants by making the parent setter protected. But even in a world where it is legal to access a protected member from an arbitrary derived class, that does not ensure that the invariants are maintained! If you allow any derived class to muck with the parenting, then you are relying upon every third-party derived class to “play nicely” and maintain your invariant. If any of them are buggy or hostile, then who knows what can happen?

When I’m faced with this kind of problem, I try to go back to first principles. Considering each design constraint leads us to an implementation decision which implements that constraint:

  • There are two kinds of abstract items — items and containers. Therefore, there should be two abstract base classes, not just one.
  • Containers are items. Therefore, the container class should derive from the item class.
  • The parenting invariant must be maintained across all containers. Therefore the invariant implementation must be inside the abstract container base class.
  • Every possible container requires “write” access to the parent state of every possible Item.  We want that access to be as restricted as possible. Ideally, we want it to be private. The only by-design way to get access to a private is to be inside the class.

And now a full solution becomes very straightforward:

using System;
using System.Collections.Generic;

public abstract class Item {
  public Item Parent { get; private set; }
  public abstract class Container : Item {
    private HashSet<Item> items = new HashSet<Item>();
    public void Add(Item item) {
      if (item.Parent != null)
        throw new Exception(“Item has inconsistent containment.”);
      item.Parent = this;
      items.Add(item);
    }
    public void Remove(Item item) {
      if (!Contains(item))
        throw new Exception(“Container does not contain that item.”);
      items.Remove(item);
      item.Parent = null;
    }
    public bool Contains(Item item) {
      return items.Contains(item);
    }
    public IEnumerable<Item> Items {
      // Do not just return items. Then the caller could cast it
      // to HashSet<Item> and then make modifications to your
      // internal state! Return a read-only sequence:
      get {
        foreach(Item item in items) yield return item;
      }
    }
  }
}

// These can be in third-party assemblies:

public class Bag : Item.Container { }
public class Box : Item.Container { }
public class Torch : Item { }
public class TreasureMap : Item { }

public class Program {
  public static void Main() {
    var map = new TreasureMap();
    var box = new Box();
    box.Add(map);
    var bag = new Bag();
    bag.Add(box);
    foreach(Item item in bag.Items)
      Console.WriteLine(item);
  }
}

Pretty slick eh?

A couple questions for you to ponder:

1) Suppose you were a hostile third party and you wanted to mess up the parenting invariant. Clearly, if you are sufficiently trusted, you can always use private reflection or unsafe code to muck around with the state directly, so that’s not a very interesting attack. Any other bright ideas come to mind for ways that this code is vulnerable to tampering?

2) Suppose you wanted to make this hierarchy an immutable collection, where “Add” and “Remove” returned new collections rather than mutating the existing collection. How would you represent the parenting relationship?

Next time, another oddity involving “protected” semantics. Have a good weekend!

 

Comments (38)

  1. Comme l’explique Eric Lippert dans son dernier post , en C# 3.0, le code suivant ne compilera pas : public

  2. Matt says:

    one way you can mess with things is to use a badly behaved GetHashCode() and/or Equals function in a new Item implementation.

    This would exploit the HashSet guts of the collection you could add something to one collection, then remove it but exploit knowledge of the number of calls to GetHashCode() and/or Equals that would be used to do:

    firstContainer.Add(nastyFoo)

    nastyFoo.ActivateChangeAssumingRemoveSemantics();

    firstContainer.Remove(nastyFoo);

    //   if (!Contains(item))  => this underlying call  to GetHashCode and Equals should behave normally

    // items.Remove(item);  => this underlying call should not, change hashcode and or Equals

    // item.Parent = null; => this neatly breaks the Parent invariant

    nastyFoo.RestoreOriginalSemantics();

    secondContainer.Add(nastyFoo);

    firstContainer.Contains(nastyFoo) && secondContainer.Contains(nastyFoo);; // true!

  3. Matt says:

    I should point out that you can guard against it by forcing the GetHashCode() and Equals() implementations in the two abstract base classes respectively, object equality is probably sufficient for this example so

    public override sealed GetHashCode()

    {

     return base.GetHashCode();

    }

    public override sealed Equals(object o)

    {

     return base.Equals(o);

    }

    (plus an IEquatable<Item> implementation if you wanted)

  4. jonskeet says:

    A slightly more mundane "attack" in terms of modeling something strange:

    box.Add(box);

    It wouldn’t cause anything hugely nasty until someone tried to walk their way up the (now broken) tree with something like:

    Item current = someItem;

    while (current != null)

    {

       // Do something with current

       current = current.Parent;

    }

  5. Thomas Danecker says:

    I’m still waiting for compiler-enforced immutable objects…

    … and a declarative (e.g. Attribute-based) way to implement GetHashCode and Equals and similar methods – i.e. have non-virtual implementations of these methods and Ignore-Attributes on fields or something alike, and implemented in a performant way (e.g. auto-generated code).

  6. Maxim Fridental says:

    The idea of having "hostile third parties", who are needed to be fighted against, is at least strange (if speaking politely). You are abusing access modifiers yourself, when you use them as a firewall agains malicious users.

    The intended usage of access modifiers is documenting your commitments about future API changes. They have one advantage against plain comments – the compliance to them can be checked automatically by compilers. And like for any other documentation, the 80/20 rule applies to them. That is, if you can’t document your commitment using access modifiers in your favorite programming language, just write a comment.

    Unfortunately, many devs think, access modifiers are a tool to protect "their" code from unintendent usage. This is very ridiculous, because of course you can never be made responsible for any problems when your code is used in an unappropriate manner. So why bother? (If you ARE being made responsible for such kind of things, change your employer).

  7. Eric Lippert says:

    I always find it strange when people who are not on the language design team tell me what the intended usage of language features is.

    I hate to be contradictory, but no, for better or for worse, access modifiers in the CLR are security features. The rules for access are thoroughly conflated with the security and type safety systems.  

    One could make the argument — I have made it myself — that this is perhaps not the best idea. One could make the argument that a better design would be to make security and type safety much more orthogonal. One could make the argument that features like Restricted Skip Visibility make the situation worse, not better. One could even argue that the CLR security system is baroque and too complicated for its own good.

    But one certainly cannot sensibly make the argument that the intended semantics of the access modifers are orthogonal to security concerns.  That is emphatically NOT how they were designed, nor is it how they were intended to be used, nor is it how they were implemented.  

    Perhaps you would like it better if that had been the design, intention or implementation.  Frankly, there are days when I would too. But that’s not the world we actually live in, so making unsupported claims to the contrary is counterproductive.

    To address your second point, of course we are responsible for the misuse of our code. Everyone who writes code has a moral responsibility to ensure that it cannot be misused to harm users. Do you really think that, say, Microsoft has no moral obligation to fix security holes in Internet Explorer?  Or that Sun has no moral obligation to fix security holes in Java?  Of course we do. We all have a moral responsibility to our users to provide software which can be used with confidence. Not to mention, we have a fiscal responsibility to our shareholders to provide the highest quality implementations we can.

    Absolutely I am responsible for the security of every bit of code I write, and absolutely I am responsible for doing everything in my power to ensure that it cannot be misused by hostile people in order to hurt the customers who trusted me to do my job. Am I responsible for the actions of hostile people? No, of course not. If they commit crimes, they’re the one’s going to jail when they’re caught, not me. But it is thoroughly specious to conclude that because I am not responsible for the crime, that I am also not responsible for doing everything in my power to prevent it!  THAT I most certainly am responsible for, and I take that responsibility very seriously.

    Accessiblity modifiers, and their integration into the .NET security system, are just one tool in my toolbox for making high-quality secure implementations of software that enforces the invariants desired by my customers. But as we’ve seen in this article, it’s just one tool, not a panacea. There’s lots of other ways that this code can get messed up; you cannot rely solely upon "private" to save you, but it is definitely a necessary step in the right direction.

  8. Frederik Siekmann says:

    Sorry, but as opposed to most things you write on your blog I don’t like the solution you presented, Eric. My major concern is: What happens if your class hierarchy gets bigger – that means many things derived from Items with individual behaviour etc. – and you still have to represent this hierarchy in one class. Not really nice imo.

    C# is missing something like a ‘friend’ keyword in C++ here. The only approach that I can think of is to make the Parent internal instead of protected.

    public abstract class Item{

     private Item _parent;

     public Item Parent {

       get { return _parent; }

       protected set { _parent = value; }

     }

    }

    But … exactly like in your previous example replacing ‘protected’ with "protected internal’ compiles at least with my VS 2008. This can’t be the other "oddity involving "protected" semantics" you mentioned, can it?

  9. Frederik Siekmann says:

    Hey I have written this comment BEFORE your answer to the presceding post – my English just sucks, so I am slow at typing. Unfair 🙁

  10. Maxim Fridental says:

    Thank you Eric for sharing to us a bit of access modifiers design. I had to speculate about why access modifiers have been included in C#, because there is no public C# design spec. So I’ve chosen the best reason I could think of. Sorry for being a little provocative.

    Security as intended usage of access modifiers… Gosh. Is it me or it is really clear for anybody, that this won’t work? Access modifiers is a language feature, not even VM feature! Hackers are not bound to any particular language or VM. So until CLR is a public standard and there are things like Mono available, you can always hack on levels much deeper than C# with its rules. Besides that, we have Reflection and we have Reflector. So you can either access private members in run-time or decompile accemblies. And I hope (HOPE) that you won’t restrict this functionality for private/protected members in next releases, because it has plenty of its legal uses.

    Sorry again, but I don’t think it is productive usage of time trying to use something that won’t work anyway. Unless it is just a mind game with the single goal to train our brain cells 🙂

    About responsibilities: I’m so happy that hammer manufacturers don’t feel themselves responsible for the possible usage of a hammer as a killing tool. Therefore, hammers are cheap and very usable because they can be kept simple. In other words, if somebody blame Microsoft for something, it doesn’t mean automatically Microsoft is responsible for that 🙂

  11. Matt says:

    Maxim I fear you have a serious lack of understanding of the specifications of the CLR.

    Protection levels on fields *are* part of the spec. If you are using reflection then you require high trust levels, if you don’t understand what that means don’t spout off about the security features controlled in part through access restrictions, it makes you look silly, especially when you’re doing it on the blog of someone who *really* knows what they are talking about…

  12. Maxim Fridental says:

    Matt,

    CLR itself is not protected. Take Mono, change it to ignore protection levels, run your "protected" software and do with it anything you want. It is a silly idea trying to achieve any security with software being executed by a hacker. In the enviroment of the hacker.

    Even native software (written on C or C++) is being easily hacked using IDA or some virtualization tools. CLR made things *simpler*, not *harder* for hackers. This is the reality how I can see it, not some abstract specification.

    Security happens only on the server side today (server side in broader sence, i.e. an interner browser is a "server" from the point of view of a malicious web site).

  13. Eric Lippert says:

    Six things:

    1) Frederik, I’m not following your point about the class hierarchy getting bigger. You can make this hierarchy get as big as you want and the invariant that items are correctly parented remains invariant. (Modulo the other problems people have mentioned of course.)  Can you expand on this?

    2) Your English is fine!

    3) C# does have “friend” semantics but only at the assembly/internal level, not at the class/private level. That is, you can say “this other assembly has access to this assemblies internals”. This is done with the InternalsVisibleTo attribute.

    4) “Until the CLR is a public standard”… but, the CLR spec _is_ a public standard, published by ECMA. I’m not following your point here.

    5) Of course you can always use a decompiler — or, for that matter, a debugger — to look at the contents of private state. Like I said before, there are any number of ways that FULLY TRUSTED code can look at private state. That’s what “fully trusted” means.  That’s not interesting.  The interesting attacks which we must protect against are attacks where PARTIALLY TRUSTED code that may be hostile is running on the system.

    6) Microsoft does not make anything so simple as a hammer, so that’s a bad analogy. Microsoft makes servers which run the businesses of massive multinational corporations. We make web browsers which hundreds of millions of people use for business and entertainment. We make productivity tools and operating systems.  All of these systems are massively more complex than a hammer.  We are not responsible for the actions of those criminals who use flaws in that software to attack our customers. But we are responsible for doing the best we possibly can to eliminate those flaws.

    Pick a better analogy. Cars, say. Car manufacturers must meet stringent safety and reliability standards so that the public which relies upon their products knows that a certain level of dilligence has gone into ensuring that the car works well even when other people abuse it. Car manufacturers cannot just say “we’ve designed this car to work well in “normal use”, and someone else crashing into you at high speed is not a normal use of this car. If someone else crashes into you, that’s their fault, and we bear no responsibility for ensuring that the design of our car is safe in that eventuality.”  Would you buy a car from a company who took that attitude towards safety?

    We take security very, very seriously here, because people DO rely upon their software being robust against misuse by hostile third parties. If we as an industry cannot get a simple invariant like “parented by its container” right, how can we possibly get complex invariants right, like “bank account information is only visible to authorized parties” ?

  14. Eric Lippert says:

    I’ve said this a couple of times but I want to emphasize it again.

    Maxim, you are misunderstanding the security meaning of "private".  "private" does not mean "no code can possibly read/write this secret".  Obviously you can read/write private data much more easily than writing your own CLR by modifying mono — you can just run a debugger.

    The security meaning of private is "not accessible to code that does NOT have sufficient access to break the security system".  

    This is like any other security system. You don’t put stuff in a vault to protect it from the people who have the combination. Nor do you put it in a vault to protect it from people who are uninterested in attacking you. You put it in a vault to protect it from people who are both hostile AND unauthorized.

  15. LINQ in Portuguese ( Direct ) http://www.linqpad.net Eric Lippert Why Can’t I Access A Protected Member

  16. Bahador says:

    Like Maxim, I always saw limiting access to members using access modifiers as a good OO practice, not a security consideration. But I agree with you Eric, that "one certainly cannot sensibly make the argument that the intended semantics of the access modifiers are orthogonal to security concerns."

    Adding to your comments about the security meaning of "private", I want to emphasize that especially in the case of a desktop application (Windows.Forms or WPF), relying on access modifiers for security enforcement is always a bad idea. Quoting Keith Brown from April 2004 edition of MSDN Mag. (http://msdn2.microsoft.com/en-us/magazine/cc163990.aspx): "I hope I’m making it clear that it’s impossible to give someone an assembly and restrict them from calling certain methods or accessing certain variables. Unless, of course, you control the machine on which they are running your code, in which case you can force them to run in a partially trusted environment. Lots of security guarantees disappear in a fully trusted environment."

    The root of the problem of course, is that almost all Windows users run apps with admin privileges, so theoretically a malicious program can tamper with all the environment settings.

  17. Frederik Siekmann says:

    1) My point with the hierarchy getting bigger was the following: What happens if you have serveral objets derived from Items that have their own way of preserving the invariant? In your example I doesn’t make that much sense but maybe the following example with the invariant "each action must be validated before it can be executed" may help:

    public abstract class Action

    {

    public void TryExecute()

    {

    if (Validate())

    Execute();

    }

    protected abstract bool Validate();

    protected abstract void Execute();

    }

    No problem if I just dervied other actions from Action and implement the abstract methods.

    But considering the following case: i have sereval actions and I just want to execute the first that works, I thougt i could implement it the following way:

    public class AlternativeActions : Action

    {

    public AlternativeActions(IEnumerable<Action> actions)

    {

    this.actions = actions.ToArray();

    }

    protected override bool Validate()

    {

    return this.actions.Any(action => action.Validate());

    }

    protected override void Execute()

    {

    this.actions.First(action => action.Validate()).Execute();

    }

    private Action[] actions;

    }

    The invariant is still holds but this won’t compile; my solution to this (you can probably imagine) was to make the abstract methods protected internal.

    This is more or less taken from a project I had half a year ago (of course heavily abstracted) and I had several actions that needed to call the protected ‘sibling’ methods and had their own implemantation of preversing the invariant; so stuffing all actions into the abstract base class was not really a solution.

    2) I reread your post. And I really had an eye-opener when I read the comment on Container.Items: I never understood why .net – collections don’t implement something like IReadOnlyCollection with properties/methods like Count or read-only accessors to the collection. I thougt by doing this you would be able to get both easy access to the (seemingly immutable) collection and security (for invariants concering the collection and the class where it is contained).

    Now I realize what was wrong about this: just stupid casting…

  18. Welcome to the forty-second issue of Community Convergence. The last few weeks have been a busy time

  19. Josh Berke says:

    I found this is an interesting post, and an interesting solution to the problem. For that thank you. I want to chime in about the discussion on access modifiers. I aggree that access modifiers won’t stop a determined hacker from messing with your code, but few things will stop a truly motivated, intelligent hacker.

    The idea of each thing you do in the name of security is to raise the bar and help protect your code / systems from one classification of user. What do I mean by this?

    95% of the code I have ever written runs on servers and is not distributed to users in the first place. Yet I use access modifiers to increase the security and safety of my code 100% of the time. Who am I protecting my code / systems from? The web developer sitting a couple of cubes over who is using my framework.

    By limiting the public surface of any API, I prevent a normal non-malicous user from accidently using my framework in an unintended way. Is it 100% fool-proof. No but nothing is.

    Maxim, you said "Sorry again, but I don’t think it is productive usage of time trying to use something that won’t work anyway. Unless it is just a mind game with the single goal to train our brain cells :)"

    I have one question for you. Do you lock the doors to your house / apartment? Why bother since it won’t stop someone from picking the lock, or bashing down the door.

    jonskeet: That is an easy attack to prevent by verifying that you are not setting the parent to itself; however, something I have struggled with in the past is what happens when you have objects: a,b,c and d.

    And a contains b which contains c which contains d which contains a. It’s the same attack just a little bit harder to defend against.

  20. David Nelson says:

    Generating a compiler error in that case is counter-intuitive to me. If that’s the way it is then that’s the way it is (and I had to verify it in VS 2008 because it definitely surprised me at first); but it certainly violates my principle of least surprise, being a long time C++ programmer, and having used C# professionally for the last six years. A protected member SHOULD be available to any arbitrary derived class; if you don’t want an arbitrary derived class accessing a protected member, then you shouldn’t make it protected in the first place. That seems pretty straightforward to me. Not sure why this scenario needed to be special cased.

  21. Eric Lippert says:

    First off, C++ has the same rule. (The statement of the rule in C++ is "A friend or a member function of a derived class can access a protected nonstatic member of one of its base clases only through a pointer to, reference to, or object of the derived class or any class derived from that class.")

    If you find this surprising about C#, you should find it equally surprising about C++.

    Second, your statement is correct — a protected member should be available to any arbitrary derived class. It is.  A protected member is available to ANY DERIVED CLASS.  That is the key point: DERIVED CLASS.  

    Is Bag a derived class of Torch?  No, it is not. Therefore, Bag does not get access to Torch’s protected member.

  22. Eric Lippert says:

    Josh: There are a number of ways to prevent cycles.  Here are just two:

    Method #1) Suppose you have A<–B<–C, all containers. (The arrow is "contained in".)  When you try to put A into C, check to see that A != C. If A is C, throw an exception. Then look up the parent list of C and see if it already contains A.  If it does, throw an exception.

    Method #2) Right now the code only allows inserts when the inserted item is not contained in anything. You could change this to say "an item may be inserted in another only if both items presently have the same parent and the items are different"  

    So, for example, suppose you have C, and A<–B.  You want to put C into B, but A and C have different parents, so that is not legal. You have to first put C into A, because A and C have the same (null) parent.  Then you can put C into B, because C and B now have the same parent.

    If you have A<–B<–C, you cannot put A into A, because they are the same.  You cannot put A into B or A into C, because they all have different parents. The only way to do that is to remove B from A, then put A into B, then put A into C, which has no cycle.

  23. Ian Marteens says:

    I’m with Maxim.

    I’ve seen, too many times, bad code written by people who thought accessibility had something to do with "preserving implementation secrets".

  24. Another very simply way in which hostile code might be able to tamper with the parenting environment (though probably not reliably) is by multithreading.  None of the methods are thread safe.  Two concurrent executions of .Add could for instance both execute the following lines simultaneously:

    if (item.Parent != null)

           throw new Exception("Item has inconsistent containment.");

    item.Parent = this;

    such that item.Parent = this; is executed twice.

    Other methods are similarly vulnerable.  If you’re not very picky about the kind of invariant violation you require this might suffice for an attack.

  25. Weeble says:

    I’m fascinated by the idea of putting one class inside another to restrict access like this. In this situation, it seems a great fit. However, I’ve found that it doesn’t apply well to other situations where I wanted to do something similar, and yet I can’t think of a better way.

    Suppose class A can generate and consume instances of classes B and C. I would like B and C to be “black boxes” to users of class A. A (horrible) way to do this is as follows:

    public sealed class C

    {
      private readonly int specialCValue;
      private C(int v) {
        specialCValue = v; 
        public sealed class B {
          private readonly string specialBValue;
          private B(int v) { specialBValue = v; }
          public sealed class A {
            public B MakeB() { … }
            public C MakeC() { … }
            public void ConsumeB(B b) { … }
            public void ConsumeC(C c) { … }
        }
      }
    }

    I think this sort of thing is was Frederick was referring to in his post. If a class really needs access to private members of more than one other class, the only solution seems to be to stack them like russian dolls.

    The thing is, I can’t figure out another way to achieve this with quite the same properties. This way, users of A can only make Bs or Cs using A, and can’t mess about with them. ConsumeB and ConsumeC can have certainty (I think) that their arguments (if not null) were made by an instance of A.

    We could do away with the nesting and make B and C public classes with internal constructors and methods for the benefit of A, but that does mean that the internals are exposed to the rest of the assembly, and in general I don’t think that’s ideal. However, it does seem to be the best solution available.

    We could make B and C private classes inside A, implementing public interfaces. But then ConsumeB and ConsumeC can only take the interface as input, anything about the class that they want to access has to be in the interface, and they can’t rely that they will actually get a well-behaved instance of B or C, unless they cast their argument, which seems pretty nasty. If B or C guaranteed useful invariants about their properties,  we can’t rely on the same from IB or IC.

    Is there a better way to do this sort of thing? I’m sorry that I’ve been so abstract, but I’m wary of posting pages and pages explaining the situations where this has come up, boring everyone to tears.

    Best regards, loving this blog,

    Weeble.

  26. Weeble says:

    Ack, the code didn’t work correctly. What’s the right way to preserve indentation?

  27. Eric Lippert says:

    I fixed the indentation. Not sure how to do it from this interface.

    It seems to me that if you want A to have access to internals of more than one class B, C in your assembly, that’s what the aptly named "internal" access modifier was invented for.

  28. Weeble says:

    Thanks. Yes, as I said, "internal" does seem to be the best that’s possible. I had just gotten my hopes up of a more rigorous solution after seeing your example of nesting classes. It would have been nice to make sure that even others working in the same project are left in no doubt that they’re not supposed to be constructing Bs and Cs, but perhaps that’s more of a policy problem than a language one! Cheers.

  29. The option using "internal" looks reasonable to me – notice that both the "mananger" who consumes and generates must be aware of the classes it manages and that the "black-box" classes must be aware of the manager; that means you’re very unlikely to be able to put the manager and the blackbox in separate assemblies anyhow due to circular dependancies.  Therefore, putting both in a single "trusted" assembly seems reasonable.  Of course, technically, the manager class might provide its encapsulation services to any sort of class (and thus breaking the circular dependancy), but I can’t really imagine such a situation in practice…

    The following would be an alternative (but it’s not pretty…)

       public class ManageP

       {

           private object key=new object();

           private static Func<P> trustedMaker;

           public static void SetPMaker(P proof, Func<P> suggestedMaker) {

               if (proof != null && trustedMaker == null) trustedMaker = suggestedMaker;

           }

           static ManageP() { P.Initialize(); }

           static void Main(string[] args)        {

               foreach (var i in Enumerable.Range(0,10))           Console.WriteLine(trustedMaker());

           }

       }

       public sealed class P

       {

           static int count=0;

           private P() { count++; }

           public override string ToString() {return "#" + count;}

           public static void Initialize() { ManageP.SetPMaker(new P(), () => new P()); }

       }

  30. I think that what I show next could be an

    elegant solution to the "friend class" design problem:

    namespace TwoMutuallyKnownClasses

    {

    public abstract class Item

    //: BaseClass, BaseInterface

    friends Bag

    //where T : SomeType

    {

    private Item _parent;

    public Item Parent

    {

    get { return _parent; }

    }

    // This method is accessible from derived classes (protected) and from friend classes (friends)

    protected friends void SetParent(Item parent)

    {

    _parent = parent;

    }

    }

    public abstract class Bag : Item

    {

    private List<Item> _list = new List<Item>();

    public void Add(Item item)

    {

    item.SetParent(this); // Not an error in C# 4.0

    _list.Add(item);

    }

    }

    }

    Something like this captures the special relationship

    between these two classes.

    The trust relationship that is expressed

    does not pervase to the containing assembly’s types,

    by lack of expressiveness of the language.

    That would be the case if the «internal» keyword had to be used,

    in this place, to express the same real relationship.

    The number of types in an assembly can easily get very large,

    and it may not be feasible to assume that they all

    share the same level of trust, as the «internal» keyword obliges.

    Also it is common that several different people contribute to a given assembly,

    possibly at different points in time,

    with decreasing knowledge of the original author’s intentions.

    Personally, I think code accessibility and intent expressiveness

    should not (need to) depend on assembly boundaries.

    Just like the «internal» keyword denotes all types of an assembly,

    the «friends» keyword would denote the set of types considered friends

    by a given class.

    Notice that the proposed expressiveness is somehow inverse

    of that of the C++ language,

    where a class «Item» says:

    "Method Bag.Add has access to me"

    or if Item trusts all of Bags methods:

    "Class Bag has access to me".

    Instead, in a CIL language (http://en.wikipedia.org/wiki/Common_Intermediate_Language)  

    we would say:

    "My method SetParent may be accessed by (any method of) any of my friend classes"

    and that:

    "Class Bag is my friend"

    Would this be a good approach?

    Would it bring any problems with it?

  31. DRBlaise says:

    I noticed no one really addressed Eric’s 2nd question about how to "make this hierarchy an immutable collection"  I could not really think of any good way to make the hierarchy immutable and have items know their parents without having to rebuild the entire hierarchy every time something changes.

    The only thing I could come up with to represent this type of immutible hierarchy was to have the container hold its child items in a binary tree structure.

  32. Weeble says:

    For the immutable question, how about introducing some sort of "view" classes? Items don’t actually store upward links to their containers, but a container can create "views" of its children, which are immutable wrappers around the items that also have an upward link back to (a view of?) the container. Of course, it could get nasty making all the view classes.

    For that matter, what should be the return value of an immutable container’s add or remove method? Suppose we have a bag inside a box, and we do "bag.Add(torch);", what’s the return value? Do we get a bag containing a torch? Do we get a bag that’s inside a box, and which contains a torch? Do we get a box containing a bag containing a torch? Are we even allowed to do this?

    How do we call the remove method? If we’re copying the hierarchy in order to make new versions, can we sensibly use a reference to identify the item we want to remove, or do we need to give our objects value semantics?

  33. Holy goodness, I’ve been busy. The MVP Summit was fabulous for us; thanks to all who attended and gave

  34. In Part Two I asked a couple of follow-up questions, the first of which was: Suppose you were a hostile

  35. Alexey says:

    I wonder if this problem (maintaining bilateral references) can be solved the way you described if you add a third type: containers that aren’t items. For example, a room can contain a box and a torch, a box can contain a sack and a a map, but nothing can can contain a room.

    Without multiple inheritance you have to extract the IContainer and IContainable interfaces and implement them in the abstract Item, Container and ContainerItem classes. Okay, you can put the Container class declaration into the Item class declaration, but what if Container is involved in another bilateral relationship?

    Can you user partial class declarations to grant access to specific internal members to a specific type?

  36. Tom says:

    I would take the easy route and give each item a Bulk property and each container a MaxCapacity property to serve as a validation tool for determining which items fit inside which containers.  Since containers are items, they too follow this "bulk" policy — as such, a room won’t fit inside a sack, but it could fit inside a house or a forest, if you were so inclined to use that level of abstraction.  (It also has the lovely side effect of providing an indicator for when a container is "full.")

    You might also consider a component design approach, wherein items have components, and components (rather than the class hierarchy) define the item’s functionality.  But I won’t go into that.  If you’re interested, here’s a good link to get you started:  http://www.enchantedage.com/node/45.  The title says XNA, but the concept is very general.

  37. Patrick says:

    I see potential threat of abusing this cross sibling calls to protected methods of the base class, but there's also similar risk of malicious calls from malicious classes that derive from one of the siblings. I mention calls to methods but it also covers accessing other things like properties.

    Here's my example – some software development company structure. Where only people of development background are allowed to give work to developers. Others have to go to the manager to get it vetted.

    public class Employee {}

    public class Developer : Employee()

    {

      protected void virtual Develop(Requirements data) { // develop }

    }

    public class CSharpDeveloper : Developer

    {

      protected  void DoSomeCSharpStuff() {}

      override void Develop(Requirements data) {

          // implement all code changes, remove from 'data' and delegate rest to other devs }

    }

    public class DbDeveloper : Developer

    {

      protected void DoSomeDbStuff() {}

      override void Develop(Requirements data) {

         // implement all db changes, remove from 'data' and delegate rest to other devs }

    }

    public class DevelopmentManager : Developer, IAmManager

    {

      DbDeveloper [] dbDevs;

      CSharpDeveloper[] cDevs;

      override void Develop(Requirements data) { // are you kidding me?! find relevant developer and delegate work }

      IAmManager.RequestWork(Requirements data) { // assess whether relevant to the devs and allocate or bin it }

    }

    public class Tester : Employee {}

    So if sibling calls were allowed then DevelopmentManager would be able to call Develop() on devs in his Team. Developers would be protected from any calls from Testers, they will have to go through proper channels 🙂

    Now someone could derive malicious class from Developer class and send malicious calls to Develop() method, which relies on access modifier and doesn't validate input as much.

    But  preventing cross sibling calls doesn't stop malicious class deriving from CSharpDeveloper class and also invoking calls.

    'Internal protected' access modifier doesn't solve the problem because it would limit this model to 1 assembly.

  38. Patrick says:

    I see potential threat of abusing this cross sibling calls to protected methods of the base class, but there's also similar risk of malicious calls from malicious classes that derive from one of the siblings. I mention calls to methods but it also covers accessing other things like properties.

    Here's my example – some software development company structure. Where only people of development background are allowed to give work to developers. Others have to go to the manager to get it vetted.

    public class Employee {}

    public class Developer : Employee()

    {

      protected void virtual Develop(Requirements data) { // develop }

    }

    public class CSharpDeveloper : Developer

    {

      protected  void DoSomeCSharpStuff() {}

      override void Develop(Requirements data) {

          // implement all code changes, remove from 'data' and delegate rest to other devs }

    }

    public class DbDeveloper : Developer

    {

      protected void DoSomeDbStuff() {}

      override void Develop(Requirements data) {

         // implement all db changes, remove from 'data' and delegate rest to other devs }

    }

    public class DevelopmentManager : Developer, IAmManager

    {

      DbDeveloper [] dbDevs;

      CSharpDeveloper[] cDevs;

      override void Develop(Requirements data) { // are you kidding me?! find relevant developer and delegate work }

      IAmManager.RequestWork(Requirements data) { // assess whether relevant to the devs and allocate or bin it }

    }

    public class Tester : Employee {}

    So if sibling calls were allowed then DevelopmentManager would be able to call Develop() on devs in his Team. Developers would be protected from any calls from Testers, they will have to go through proper channels 🙂

    Now someone could derive malicious class from Developer class and send malicious calls to Develop() method, which relies on access modifier and doesn't validate input as much.

    But  preventing cross sibling calls doesn't stop malicious class deriving from CSharpDeveloper class and also invoking calls.

    'Internal protected' access modifier doesn't solve the problem because it would limit this model to 1 assembly.

Skip to main content