您可以使用下面显示的技巧在 Bob 上创建一个方法 FriendRecieveMessageFromAlice,该方法只能由 Alice 调用。一个邪恶的类 Eve 将无法在不使用私有成员反射的情况下调用该方法。
我很想知道其他人之前是否建议过这个或其他解决方案。几个月来我一直在寻找解决这个问题的方法,但我从来没有见过一个能确保真正的 friend 语义的解决方案,前提是不使用反射(你几乎可以用它规避任何事情)。
爱丽丝和鲍勃
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();
}
}
夏娃
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);
}
}
工作原理
诀窍在于方法Bob.FriendRecieveMessageFromAlice 需要一个(虚拟)泛型类型参数作为标记。该泛型类型必须继承自 Alice 和虚拟接口 IKey。
由于Alice 本身没有实现IKey,调用者需要提供Alice 的一些子类,它确实实现了IKey。但是Alice只有私有构造函数,所以它只能被嵌套类子类化,不能被其他地方声明的类子类化。
这意味着只有嵌套在Alice 中的类才能对其进行子类化以实现IKey。这就是AlicePrivateKey 所做的,因为它被声明为私有,所以只有Alice 可以将它作为通用参数传递给Bob.FriendRecieveMessageFromAlice,所以只有Alice 可以调用该方法。
然后我们反过来做同样的事情,这样只有Bob可以调用Alice.FriendRecieveMessageFromBob。
密钥泄露
值得注意的是,Bob.FriendRecieveMessageFromAlice 在调用时可以访问TKey 泛型类型参数,并且可以使用它来欺骗来自Alice 的另一个方法OtherClass.OtherMethod<OtherTkey> 接受OtherTKey : Alice, IKey 的调用。因此,让密钥从不同的接口继承会更安全:Alice, IBobKey 用于第一个接口,Alice, IOtherKey 用于第二个接口。
比 C++ 好朋友
- 即使
Bob本身也不能调用自己的方法Bob.FriendRecieveMessageFromAlice。
-
Bob 可以通过不同的好友方法拥有多个好友:
// 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 { }
我很想知道是否有某种方法可以比蛮力试错更有效地找到此类技巧。某种“C# 类型系统的代数”,它告诉我们哪些限制可以强制执行,哪些不能强制执行,但我还没有看到任何关于此类主题的讨论。