c++的friend关键字允许类a将类B指定为它的friend。这允许类B访问类A的私有/受保护成员。

我从来没有读过任何关于为什么c#(和VB.NET)中不包含这个的东西。大多数关于StackOverflow问题的答案似乎都在说它是c++的一个有用的部分,并且有很好的理由使用它。以我的经验,我不得不同意。

对我来说,另一个问题似乎是在问如何在c#应用程序中做类似于friend的事情。虽然答案通常围绕嵌套类,但它似乎没有使用friend关键字那么优雅。

最初的《设计模式》一书在其示例中经常使用它。

总之,为什么在c#中没有friend,在c#中模拟friend的“最佳实践”方法是什么?

(顺便说一下,internal关键字不是一回事,它允许整个程序集中的所有类访问内部成员,而friend允许你给某个类完全访问另一个类)


当前回答

事实上,c#提供了在纯面向对象的方式下获得相同行为的可能性,没有特殊的词语——它的私有接口。

至于这个问题,c#中朋友的对等物是什么?这篇文章被标记为重复,没有人提出真正好的实现-我将在这里给出这两个问题的答案。

主要思想是从这里开始的:什么是私有接口?

比方说,我们需要一些类来管理另一个类的实例,并对它们调用一些特殊的方法。我们不想给任何其他类调用这个方法的可能性。这与friend c++关键字在c++世界中所做的事情完全相同。

我认为在实际实践中的一个很好的例子是全状态机模式,其中一些控制器更新当前状态对象并在必要时切换到另一个状态对象。

你可以:

使Update()方法公开的最简单和最糟糕的方法是希望 每个人都明白它为什么不好。 第二种方法是将其标记为内部。如果你把你的 类到另一个程序集,但即使是该程序集中的每个类 可以调用每个内部方法。 使用私有/受保护的接口——我就是这样做的。

Controller.cs

public class Controller
{
    private interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() {  }
    }

    public Controller()
    {
        //it's only way call Update is to cast obj to IState
        IState obj = new StateBase();
        obj.Update();
    }
}

Program.cs

class Program
{
    static void Main(string[] args)
    {
        //it's impossible to write Controller.IState p = new Controller.StateBase();
        //Controller.IState is hidden
        var p = new Controller.StateBase();
        //p.Update(); //is not accessible
    }
}

那么遗产呢?

我们需要使用在显式接口成员实现中描述的技术,因为不能将其声明为虚拟并将IState标记为受保护,以提供从Controller派生的可能性。

Controller.cs

public class Controller
{
    protected interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() { OnUpdate(); }
        protected virtual void OnUpdate()
        {
            Console.WriteLine("StateBase.OnUpdate()");
        }
    }

    public Controller()
    {
        IState obj = new PlayerIdleState();
        obj.Update();
    }
}

PlayerIdleState.cs

public class PlayerIdleState: Controller.StateBase
{
    protected override void OnUpdate()
    {
        base.OnUpdate();
        Console.WriteLine("PlayerIdleState.OnUpdate()");
    }
}

最后是如何测试类Controller抛出继承的例子: ControllerTest.cs

class ControllerTest: Controller
{
    public ControllerTest()
    {
        IState testObj = new PlayerIdleState();
        testObj.Update();
    }
}

希望我涵盖了所有情况,我的回答是有用的。

其他回答

从。net 3开始就有了InternalsVisibleToAttribute,但我怀疑他们只是在单元测试兴起后才添加它来迎合测试程序集。我想不出还有什么其他理由来使用它。

它在组装层工作但它能完成内部不能完成的工作;也就是说,您希望分发一个程序集,但希望另一个非分布式程序集具有对该程序集的特权访问。

很正确地,他们要求好友程序集是强键的,以避免有人在你受保护的程序集旁边创建一个假装的好友。

这种友谊也可以通过使用“代理”——一些内部类来模拟。考虑下面的例子:

public class A // Class that contains private members
{
  private class Accessor : B.BAgent // Implement accessor part of agent.
  {
    private A instance; // A instance for access to non-static members.
    static Accessor() 
    { // Init static accessors.
      B.BAgent.ABuilder = Builder;
      B.BAgent.PrivateStaticAccessor = StaticAccessor;
    }
    // Init non-static accessors.
    internal override void PrivateMethodAccessor() { instance.SomePrivateMethod(); }
    // Agent constructor for non-static members.
    internal Accessor(A instance) { this.instance = instance; }
    private static A Builder() { return new A(); }
    private static void StaticAccessor() { A.PrivateStatic(); }
  }
  public A(B friend) { B.Friendship(new A.Accessor(this)); }
  private A() { } // Private constructor that should be accessed only from B.
  private void SomePrivateMethod() { } // Private method that should be accessible from B.
  private static void PrivateStatic() { } // ... and static private method.
}
public class B
{
  // Agent for accessing A.
  internal abstract class BAgent
  {
    internal static Func<A> ABuilder; // Static members should be accessed only by delegates.
    internal static Action PrivateStaticAccessor;
    internal abstract void PrivateMethodAccessor(); // Non-static members may be accessed by delegates or by overrideable members.
  }
  internal static void Friendship(BAgent agent)
  {
    var a = BAgent.ABuilder(); // Access private constructor.
    BAgent.PrivateStaticAccessor(); // Access private static method.
    agent.PrivateMethodAccessor(); // Access private non-static member.
  }
}

如果只用于访问静态成员,则会简单得多。 这种实现的好处是,所有类型都在友谊类的内部作用域中声明,并且与接口不同,它允许访问静态成员。

我读过许多关于“朋友”关键字的聪明评论,我同意它是有用的东西,但我认为“内部”关键字没有那么有用,而且它们对纯OO编程来说都很糟糕。

我们有什么?(说到“朋友”,我也说到“内部”)

使用“friend”是否会使代码对于oo不那么纯粹? 是的, 不使用“friend”会使代码更好吗? 不,我们仍然需要在类之间建立一些私人关系,而且只有打破了我们美丽的封装才能做到,所以这也不好,我可以说它比使用“朋友”更邪恶。

使用friend会产生一些局部问题,不使用friend会给代码库用户带来问题。

在我看来,编程语言的通用解决方案是这样的:

// c++ style
class Foo {
  public_for Bar:
    void addBar(Bar *bar) { }
  public:
  private:
  protected:
};

// c#
class Foo {
    public_for Bar void addBar(Bar bar) { }
}

你觉得怎么样?我认为这是最常见的纯面向对象的解决方案。你可以对你想要的任何类打开任何方法的访问。

你可以用c#关键字“internal”来接近c++的“朋友”。

您可以保持它为私有,并使用反射来调用函数。测试框架可以做到这一点,如果你要求它测试一个私有函数