C# 等同于 C++ friend 关键字?

标签 c# oop friend

我是 C# 的新手,我有一个问题,在 C++ 中我通常会使用 friend 标识符。现在我知道 C# 中不存在 friend 关键字,但我没有任何解决此问题的经验(除了使所有类变量成为公共(public)属性,如果我可以)。

我有以下场景:

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*/
}

有没有其他巧妙的方法,无需公开 B.m_instanceOfA,就可以让 C 访问此变量(仅在构造函数中)?

最佳答案

您可以使用下面显示的技巧来创建一个方法 FriendRecieveMessageFromAlice在 Bob 上只能由 Alice 调用.一个邪恶的类(class),Eve ,如果不对私有(private)成员使用反射,将无法调用该方法。

我很想知道其他人之前是否提出过这种或另一种解决方案。几个月来我一直在寻找解决该问题的方法,但我从未见过能确保真实的 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只有私有(private)构造函数,因此它只能被嵌套类子类化,而不能被其他地方声明的类子类化。

这意味着只有一个类嵌套在Alice中可以将其子类化以实现 IKey .这就是AlicePrivateKey确实如此,并且由于它被声明为私有(private),所以只有 Alice可以将其作为通用参数传递给 Bob.FriendRecieveMessageFromAlice , 所以只有 Alice可以调用该方法。

然后我们反过来做同样的事情,这样只有Bob可以调用Alice.FriendRecieveMessageFromBob .

泄露 key

值得注意的是,调用时,Bob.FriendRecieveMessageFromAlice可以访问 TKey通用类型参数,并且可以使用它来欺骗来自 Alice 的调用在另一种方法上 OtherClass.OtherMethod<OtherTkey>接受OtherTKey : Alice, IKey .因此,让 key 从不同的接口(interface)继承会更安全:Alice, IBobKey首先,Alice, IOtherKey第二个。

比 C++ friend 更好

  • 甚至 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# 类型系统的代数”,告诉我们可以强制执行哪些限制,哪些不能强制执行,但我还没有看到任何关于此类主题的讨论。

关于C# 等同于 C++ friend 关键字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19135534/

相关文章:

c# - 如何清除 C# Flags Enum 中的高字节

c# - Silverlight 动态资源文件位置

JavaScript 原型(prototype)继承 - 函数未呈现正确的警报

c++ - 模板类的友元函数需要访问成员类型

C++:避免​​在公共(public)接口(interface)中使用特定于库的类型

javascript - 使用 javascript 选择客户端 ListView 中的所有复选框

c# - 自定义 ScrollBar 样式神器?

java - 为什么这个类中的某些构造函数使用 this(context) 而不是 super(context)?

c# - 全局静态类和方法不好吗?

c++ - 将流运算符与模板类一起使用