You can use the trick shown below to create a FriendRecieveMessageFromAlice method in Bob that can only be called by Alice . The evil class, Eve , cannot invoke this method without using reflection for private members.
I am curious to know if this or another solution was previously proposed by other people. I searched for a solution to this problem for several months, and I never saw what provided real semantics for friend , provided that reflection is not used (you can get around almost 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 {
How it works
The trick is that the Bob.FriendRecieveMessageFromAlice method requires a parameter (dummy) of a generic type that serves as a token. This generic type should inherit from both Alice and the dummy IKey interface.
Since Alice does not implement IKey itself, the caller needs to provide some subclass of Alice that implements IKey . However, Alice has only private constructors, so it can only be subclassed by nested classes, not classes declared elsewhere.
This means that only a class nested in Alice can subclass it to implement IKey . What makes AlicePrivateKey , and since it is declared private, only Alice can pass it as a general argument to Bob.FriendRecieveMessageFromAlice , so only Alice can call this method.
Then we do the same thing on the contrary, so that only Bob can call Alice.FriendRecieveMessageFromBob .
Key leak
It should be noted that when calling Bob.FriendRecieveMessageFromAlice has access to the generic type TKey and can use it to substitute a call from Alice using another method OtherClass.OtherMethod<OtherTkey> that accepts OtherTKey : Alice, IKey . Therefore, it would be safer to make keys inherited from different interfaces: Alice, IBobKey for the first and Alice, IOtherKey for the second.
Better than C ++ friend
- Even
Bob himself cannot call his own Bob.FriendRecieveMessageFromAlice method. Bob can have several friends with different friends 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 would be interested to know if there is a way to find such tricks in a more efficient way than the trial version and the error. Some kind of "algebra of a system like C #", which tells us what restrictions can be applied and what cannot, but I have not seen any discussions on this topic.