C#等同于C++好友关键字?

Yel*_*low 8 c# oop friend

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

我有以下场景:

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*/
}
Run Code Online (Sandbox Code Playgroud)

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

Suz*_*ron 11

您可以使用下面显示的技巧FriendRecieveMessageFromAlice在Bob上创建一个只能被调用的方法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();
    }
}
Run Code Online (Sandbox Code Playgroud)

前夕

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个怎么运作

诀窍是该方法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>接受a的方法的调用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 { }
    
    Run Code Online (Sandbox Code Playgroud)

我有兴趣知道是否有某种方法可以比蛮力的试验和错误更有效地找到这样的技巧.某种"C#类型系统的代数",它告诉我们可以强制执行哪些限制,哪些不可以,但我没有看到任何关于这类主题的讨论.


Yve*_* M. 7

内部

您可以使用internal关键字.然后,您的类型(或类型成员)将仅对同一程序集中的其他类型可见; 并且:

如果需要从其他程序集中看到内部类型,可以使用InternalsVisibleToAttribute.此属性以整个程序集为目标,通常写在AssemblyInfo.cs文件中.


PS:C#中不存在Friend关键字,但友谊的概念存在(与C++中的不完全相同),它在MSDN 的Friend Assemblies文章中有所描述.另请注意,在VB.NET中存在friend关键字,其行为与C#internal关键字完全相同.