8

I am new to C#, and I have a problem for which in C++ I would normally use the friend identifier. Now I know the friend keyword doesn't exist in C#, but I don't have any experience with how to work around this (except for making all class variables public properties, which I want to avoid if I can).

I have the following scenario:

public class A 
{
    public string Info { get; set; }
    /* much more data */
}

public class B
{
    private A m_instanceOfA;

    public B(A a) { m_instanceOfA = a; }

    public Info { get return A.info; set A.Info  = value; }

    /* And some more data of its own*/
}

public class C
{
    private A m_instanceOfA;

    // I need a constructor of C, which needs to set C.m_instanceOfA
    // to the same value as b.m_instanceOfA.
    public C(B b) { m_instanceOfA = b.m_instanceOfA ; } // <--- Not allowed!

    /* And some more data of its own*/
}

Is there any other clever way, without making B.m_instanceOfA public, to give C access to this variable (only in the constructor)?

Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
Yellow
  • 3,955
  • 6
  • 45
  • 74
  • 7
    This quastion has already been asked: http://stackoverflow.com/questions/204739/what-is-the-c-sharp-equivalent-of-friend Good luck – ransh84 Oct 02 '13 at 11:12
  • 1
    It came across that question before and it is fairly similar indeed, but I thought that this particular case might yield some other interesting suggestions and/or examples. I have little experience with C# code design, and I figured that this is something that would occur pretty often. If you feel the questions are too similar though, I'll mark it as a duplicate. – Yellow Oct 02 '13 at 11:17
  • possible duplicate of [Why does C# not provide the C++ style 'friend' keyword?](http://stackoverflow.com/questions/203616/why-does-c-sharp-not-provide-the-c-style-friend-keyword) – Sriram Sakthivel Oct 02 '13 at 11:25

6 Answers6

16

You can use the trick shown below to create a method FriendRecieveMessageFromAlice on Bob that can only be called by Alice. An evil class, Eve, won't be able to call that method without using reflection on private members.

I'm curious to know if this or another solution have been suggested before by other people. I've been looking for a solution to that problem for months, and I never saw one that ensured real friend semantics provided that reflection isn't used (you can circumvent nearly anything with it).

Alice and Bob

public interface IKey { }

public class Alice
{
    // Alice, Bob and Carol must only have private constructors, so only nested classes can subclass them.
    private Alice() { }
    public static Alice Create() { return new Alice(); }

    private class AlicePrivateKey : Alice, IKey { }

    public void PublicSendMessageToBob() {
        Bob.Create().FriendRecieveMessageFromAlice<AlicePrivateKey>(42);
    }

    public void FriendRecieveMessageFromBob<TKey>(int message) where TKey : Bob, IKey {
        System.Console.WriteLine("Alice: I recieved message {0} from my friend Bob.", message);
    }
}

public class Bob
{
    private Bob() { }
    public static Bob Create() { return new Bob(); }

    private class BobPrivateKey : Bob, IKey { }

    public void PublicSendMessageToAlice() {
        Alice.Create().FriendRecieveMessageFromBob<BobPrivateKey>(1337);
    }

    public void FriendRecieveMessageFromAlice<TKey>(int message) where TKey : Alice, IKey {
        System.Console.WriteLine("Bob: I recieved message {0} from my friend Alice.", message);
    }
}

class Program
{
    static void Main(string[] args) {
        Alice.Create().PublicSendMessageToBob();
        Bob.Create().PublicSendMessageToAlice();
    }
}

Eve

public class Eve
{
    // Eve can't write that, it won't compile:
    // 'Alice.Alice()' is inaccessible due to its protection level
    private class EvePrivateKey : Alice, IKey { }

    public void PublicSendMesssageToBob() {
        // Eve can't write that either:
        // 'Alice.AlicePrivateKey' is inaccessible due to its protection level
        Bob.Create().FriendRecieveMessageFromAlice<Alice.AlicePrivateKey>(42);
    }
}

How it works

The trick is that the method Bob.FriendRecieveMessageFromAlice requires a (dummy) generic type parameter which serves as a token. That generic type must inherit from both Alice, and from a dummy interface IKey.

Since Alice does not implement IKey itself, the caller needs to provide some subclass of Alice which does implement IKey. However, Alice only has private constructors, so it can only be subclassed by nested classes, and not by classes declared elsewhere.

This means that only a class nested in Alice can subclass it to implement IKey. That's what AlicePrivateKey does, and since it is declared private, only Alice can pass it as the generic argument to the Bob.FriendRecieveMessageFromAlice, so only Alice can call that method.

We then do the same thing the other way round so that only Bob can call Alice.FriendRecieveMessageFromBob.

Leaking the key

It is worth noting that, when called, Bob.FriendRecieveMessageFromAlice has access to the TKey generic type parameter, and could use it to spoof a call from Alice on another method OtherClass.OtherMethod<OtherTkey> accepting a OtherTKey : Alice, IKey. It would therefore be safer to make the keys inherit from distinct interfaces: Alice, IBobKey for the first, and Alice, IOtherKey for the second.

Better than C++ friend

  • Even Bob itself can't call its own method Bob.FriendRecieveMessageFromAlice.
  • Bob can have multiple friends with distinct friend methods:

    // Can only be called by Alice, not by Carol or Bob itself
    Bob.FriendRecieveMessageFromAlice <TKey>(int message) where TKey : Alice, IKey { }
    // Can only be called by Carol, not by Alice or Bob itself
    Bob.FriendRecieveMessageFromCarol <TKey>(int message) where TKey : Carol, IKey { }
    

I'd be interested to know if there is some way to find tricks like this in a more efficient way than brute-force trial and error. Some kind of "algebra of C#'s type system", that tells us what restrictions can be enforced and what can't, but I haven't seen any discussion on that kind of topic.

Suzanne Soy
  • 3,027
  • 6
  • 38
  • 56
  • 1
    This is a quite clever use of the type system, but I don't think you can make a given method have two friends since the where clause of the constraint cannot have an || operator defined in it. https://msdn.microsoft.com/en-us/library/d5x73970.aspx – Mike Mar 10 '16 at 17:12
  • @Mike I think you're right. You could always declare several wrappers for the method, one for each “friend”, although this will result in even more “cleverbloat” :) . If you have many methods or many friends, you could instead have just one “factory” method which returns an inner class containing all the friend methods, see an example here: https://dotnetfiddle.net/n30ZtX . This should be called the Ugly design pattern :) . – Suzanne Soy Mar 10 '16 at 18:25
  • A private singleton (of an exposed class, of course) would let you invoke the function with less obfuscation (avoid generics and type constraints altogether) and still achieving your "Better than C++ friend" goals. See my answer. :) – monkey0506 Jul 13 '17 at 14:20
  • This is pretty clever. Only thing is that, anyone can make a key by making a class that inherits the class and interface, which is a waste of the singular inheritince use. One way around that though is to make the class sealed, then it would be foolproof. – AustinWBryan Jul 24 '18 at 11:11
  • Another thing that's better than the C++ version is that this is per method based, so instead of Bob seeing *all* of Alice's method, when he only needed to see one, now, he only sees what he needs. I like that better. – AustinWBryan Jul 24 '18 at 11:12
  • But, now that I think about it, it'll still show up in intellisense, which exposes it to the other classes, which is kinda a downside – AustinWBryan Jul 24 '18 at 12:08
9

Internal

You can use the internal keyword. Your type (or type member) will then only be visible to other types within the same assembly; And also:

If you need your internal types to be visible from other assemblies, you can use the InternalsVisibleToAttribute. This attribute targets your whole assembly and is usually written in the AssemblyInfo.cs file.


PS: Friend keyword doesn't exists in C# but the concept of friendship exists (not exactly the same as the one in C++), it is described on the Friend Assemblies article from the MSDN. Note also that a friend keyword exists in VB.NET and has the exact same behaviour than the C# internal keyword.

Yves M.
  • 29,855
  • 23
  • 108
  • 144
2

You can only use 5 accessibility modifiers:

public Access is not restricted.

protected Access is limited to the containing class or types derived from the containing class.

internal Access is limited to the current assembly.

protected internal Access is limited to the current assembly or types derived from the containing class.

private Access is limited to the containing type.

albertjan
  • 7,739
  • 6
  • 44
  • 74
Anarion
  • 2,406
  • 3
  • 28
  • 42
2

I modified the code you posted, so it should work as you want exactly:

using System.Reflection;
using System.Diagnostics;

public class A 
{
    public string Info { get; set; }
    /* much more data */
}

public class B
{
    private A m_instanceOfA;
    public string Info { get; set; }

    public B(A a) => Info = a;

    private readonly ConstructorInfo friend = typeof(C).GetConstructor(new Type[] { typeof(B) });
    public A InstanceOfA
    {
        get
        {
            if (new StackFrame(1).GetMethod() != friend)
               throw new Exception("Call this property only inside the constructor of C");
            return this.m_instanceOfA;
        }
    }
}

public class C
{
    private A m_instanceOfA;

    // Only the constructor of C can set his m_instanceOfA
    // to the same value as b.m_instanceOfA.
    public C(B b)
    {
        Info = b.InstanceOfA; // Call the public property, not the private field. Now it is allowed and it will work too, because you call it inside the constructor of C. In Main method, for example, an exception will be thrown, if you try to get InstanceOfA there.
    }
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
1

I think you're looking for the "internal" keyword - basically only visible to classes in the same assembly

Alternatively you could so something like (excuse the method names!) :

public interface IAmAFriendOfB {
   void DoSomethingWithA(A instanceOfA);
}

public class B {
    private A m_instanceOfA;

    public B(A a) { m_instanceOfA = a; }

    public void BeFriendlyWith(IAmAFriendOfB friend) {
       friend.DoSomethingWithA(m_instanceOfA);
    }

    // the rest of your class
}

public class C : IAmAFriendOfB {

    private A m_instanceOfA;

    public C(B b) {
        b.BeFriendlyWith(this);
    }

    void DoSomethingWithA(A instanceOfA) {
        m_instanceOfA = b.m_instanceOfA;
    }   
}
Martin Ernst
  • 5,629
  • 2
  • 17
  • 14
  • That is a good suggestion indeed, useful in my current case. But let us assume that they might not be in the same assembly, are there any other options left? – Yellow Oct 02 '13 at 11:14
  • I updated my answer with a way you can do it using an interface – Martin Ernst Oct 03 '13 at 09:07
  • Ha, this is quite a fantastic option! But am I right in saying that `class C` has `A m_instanceOfA` and `B b` as class data? It's not entirely clear to me, and would be good to add for clarity's sake. – Yellow Oct 03 '13 at 09:25
1

Here's another alternative using an internal class with a private singleton instance, which allows you to fine-tune which methods are exposed to the pseudo-friend class.

using System;

namespace Test
{
    public class A 
    {
        public string Info { get; set; }
        /* much more data */
    }

    public class B
    {
        private A m_instanceOfA;

        public B(A a) { m_instanceOfA = a; }

        public string Info
        {
            get { return m_instanceOfA.Info; }
            set { m_instanceOfA.Info = value; }
        }

        // requires an instance of a private object, this establishes our pseudo-friendship
        internal A GetInstanceOfA(C.AGetter getter) { return getter.Get(m_instanceOfA); }

        /* And some more data of its own*/
    }

    public class C
    {
        private A m_instanceOfA;

        private static AGetter m_AGetter; // initialized before first use; not visible outside of C

        // class needs to be visible to B, actual instance does not (we call b.GetInstanceOfA from C)
        internal class AGetter
        {
            static AGetter() { m_AGetter = new AGetter(); } // initialize singleton

            private AGetter() { } // disallow instantiation except our private singleton in C

            public A Get(A a) { return a; } // force a NullReferenceException if calling b.GetInstanceOfA(null)
        }

        static C()
        {
            // ensure that m_AGetter is initialized
            System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(AGetter).TypeHandle);
        }

        public C(B b)
        {
            m_instanceOfA = b.GetInstanceOfA(m_AGetter);
        }

        public string Info
        {
            get { return m_instanceOfA.Info; }
            set { m_instanceOfA.Info = value; }
        }

        /* And some more data of its own*/
    }

    public class Test
    {
        public static void Main()
        {
            A a = new A();
            B b = new B(a);
            C c = new C(b);
            c.Info = "Hello World!";
            Console.WriteLine(a.Info);
        }
    }
}

Live Demo

The C.AGetter class cannot be instantiated outside of itself, so C.m_AGetter (which is both private and static) represents a singleton instance that is only accessible from within C. As B.GetInstanceOfA requires an instance of C.AGetter, this makes the function useless outside of C. The function is marked internal to minimize its exposure, but the argument should also act as a form of self-documentation that it isn't meant for common use.

The interface alternative risks exposing methods beyond their intended scope (e.g., a class implementing the interface where it should not have access to the exposed methods), while this approach prevents that. Naysayers of friend access may still object to it, but this keeps things much closer to the intended scope.

monkey0506
  • 2,489
  • 1
  • 21
  • 27