0

I quite like the friendship feature of C++. Say, we have a Parent and Child. Child's ctor takes a Parent as a parameter:

Child::Child(Parent & parent)
{
    // Parent::RegisterChild is private
    parent.RegisterChild(this);
}

Due to friendship, Parent and Child may maintain quite secure control over themselves - for example, when Child changes Parent, it may inform previous Parent, that it wants to be detached from its Child list and the new Parent, that it wants to be attached to its list. The security in this solution means, that no class deriving from Parent or Child may break this mechanism.

Unfortunately, C# does not provide such feature as friendship. There is a internal access modifier, but it restricts visibility of classes elements to the assembly, so if one extends the assembly and introduces new classes, the mechanism will no longer be safe. And extracting such classes to separate assembly just to provide security seems to be a very messy idea.

Is there a way to use existing C# mechanisms to provide such close and secure cooperation between two classes, which cannot be broken in derived classes?


Edit: In response to comments

This is not a matter of trust, because - as Eric stated - people, who have access to source code can always break it (remove private modifiers, add another friend classes, whatever). This is a security measure designed to prevent people from making simple, stupid mistakes, which are later hard to track. I use friends to create isolated mechanisms, embedded in base classes, which cannot be (or at least cannot easily) be broken in derived classes. That way neither I nor my coworkers don't have to worry about these anymore.

Spook
  • 25,318
  • 18
  • 90
  • 167
  • I've implemented friend-like feature in C# using `private` access-specifier and using an interface named `IFriendKey`. – Nawaz Oct 08 '13 at 05:51
  • 3
    Not "safe" against what attack? "internal" isn't a *security* feature in the first place, so speaking of "safety" is a non-starter. If your coworkers can't use internal correctly then what makes you think they'd use "friend" correctly? for that matter, if you don't trust your coworkers to use your internal members correctly then why are you giving them write access to the source code? – Eric Lippert Oct 08 '13 at 06:23
  • 1
    @EricLippert: By "safe", he probably meant the same thing which you achieve by making some part of your code `private`, some other `protected`, etc. In other words, if you *trust* your coworkers, then does it mean you don't use `private` ever? – Nawaz Oct 08 '13 at 07:23
  • @Nawaz: I don't use `private` because I don't trust my coworkers. I use `private` to *communicate* to my coworkers that a particular bit of code is an implementation detail that is subject to change. If they do the due diligence and decide that the cost of taking a dependency on that implementation detail is good value for the cost, then they can change it to `internal`. – Eric Lippert Oct 08 '13 at 13:53
  • @EricLippert: Now I guess you understand what the OP (probably) meant? – Nawaz Oct 08 '13 at 13:56
  • @Nawaz: I do; my point is that a type system is not required to provide a mechanism for solving every process problem. Communicating that a particular member should be used only by another particular class can be solved in practice with a combination of `internal`, comments and code reviews. There has to be a line drawn somewhere and `internal` is a great place to draw the line. – Eric Lippert Oct 08 '13 at 15:11
  • @EricLippert: I understand that there could some kind of workaround if you don't have `friend` feature supported by the language. The point is that whatever workaround you come up with is not as good as `friend` feature of C++, because the compiler is unable to help you when you go wrong and call the `dont_touch_me()` function. Also note that whatever you said can be said for a language which doesn't have features like access specifiers (such as Python). – Nawaz Oct 08 '13 at 15:57
  • @Nawaz: Sure, but Python is different because the problems it seeks to solve effectively are different. Moreover it is *always* the case that a compiler feature could be implemented to enforce a business process. For example, imagine an access modifier called `approved`. Code which has been approved by dev and test review teams gets the modifier. The compiler enforces the rule "code which is not approved may not be called by any approved code". Awesome feature. It clearly improves on `friend`. Why doesn't C++ implement it? – Eric Lippert Oct 08 '13 at 18:30
  • @Nawaz: We can come up with such features all day; at some point you have to draw a line and say that there is not sufficiently general value to make the compiler enforce this process policy. Some mechanism outside the language needs to enforce that policy. – Eric Lippert Oct 08 '13 at 18:33
  • @EricLippert: You're deviating from the main point. The question is **not** about why C# doesn't support friend feature? Please read your first comment in which you're attempting to overlook the advantages of `friend` feature (though now you *tacitly* admits that C# is *missing* a good feature which C++ supports and funnily compares this with "C++ is missing an *awesome* feature which no language supports!"). – Nawaz Oct 08 '13 at 18:41
  • @Nawaz: The question is "how can I work around the lack of this feature?" and the answer is "a combination of `internal` and code reviews". My point is that `friend` enforces in the compiler what I would characterize as a relationship between *coworkers*, not between *classes*. A friend relationship essentially means "the developers of these two classes have to agree on how they work". That's what `internal` means, albeit at a different level of granularity. I'm not suggesting that `friend` is a bad feature (though I could criticize it), just that it is unnecessary. – Eric Lippert Oct 08 '13 at 18:57
  • @EricLippert: *A friend relationship essentially means "the developers of these two classes have to agree on how they work"*. Sorry you twisted C++ `friend` to support your statement *"That's what internal means"*. And from Python's (the mother C's) perspective, even `private` and `protected` are unnecessary, so why do C# have these? – Nawaz Oct 08 '13 at 19:03
  • @Nawaz: You could choose to take the cooperative position that we're all here to help and that we all learn a lot from thoughtfully considering each other's perspectives. Or you could choose to take the competitive and hostile position that two different perspectives mean that one is right and the other is "twisted" to defend a position from attack. My advice to you is that you take the former position on StackOverflow. – Eric Lippert Oct 08 '13 at 19:09
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/38821/discussion-between-eric-lippert-and-nawaz) – Eric Lippert Oct 08 '13 at 19:10

2 Answers2

3

The only type-based access would be nested types. If you nest one type within another, the nested type has access to all the members of the enclosing class, including private members. The nested type can also be private. For example:

public class Outer
{
    // Code outside the body of Outer cannot call this. But
    // the code in Nested *is* inside the body of Outer, so it's okay.
    private void Foo()
    {
    }

    private class Nested
    {
        internal void Bar()
        {
            new Outer().Foo();
        }
    }
}

That can be helpful in some cases, but obviously isn't a general replacement for the concept of "friend" classes in C++.

One thing to be aware of is InternalsVisibleToAttribute, which allows one assembly access to the internal members of another. I realize in your case you actually want more restricted access than internal, but [InternalsVisibleTo] is still worth knowing about - it's particularly useful for testing.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
0

Here is the basic idea of what I've implemented for my project and this works great. Hopefully it will work for you too. Note that this enforces friendship only at runtime (which doesn't sound great).

public interface IFriendKey { object Id {get; set;} }

class Friend<TFriend>
{
    protected void FriendAssert(IFriendKey key)
    {
        if ( key == null || key.Id == null || key.Id.GetType() != typeof(TFriend) )
            throw new Exception("No right to execute the called method.");
    }
}

class A : Friend<B>
{
    public void f(IFriendKey key)
    {
        FriendAssert(key);
        Console.WriteLine("ONLY class B can execute this method successfully, even though it is declared public.");
    }
}

class B
{
    private class AFriendKey : IFriendKey 
    {
        public object Id {get; set;}
    }

    IFriendKey Key { get { return new AFriendKey() {Id = this}; } }

    public void g()
    {
        new A().f(this.Key); 
    }
}

public class Test
{
    public static void Main()
    {
        new B().g();
    }
}

Online Demo.

Hope that helps.

Nawaz
  • 353,942
  • 115
  • 666
  • 851