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

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

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

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

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

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


当前回答

顺便说一句。 使用friend并不是违反封装,相反,它是加强封装。就像访问器+突变器、操作符重载、公共继承、向下强制转换等一样,它经常被误用,但这并不意味着关键字没有,或者更糟,没有一个坏的目的。

请参阅Konrad Rudolph在另一个线程中的消息,或者如果您愿意,请参阅c++常见问题解答中的相关条目。

其他回答

拥有编程领域的朋友或多或少被认为是“肮脏的”,容易被滥用。它破坏了类之间的关系,破坏了OO语言的一些基本属性。

也就是说,这是一个很好的特性,我自己在c++中使用过很多次;并且也想在c#中使用它。但我敢打赌,因为c#的“纯”OOness(与c++的伪OOness相比),MS决定因为Java没有朋友,关键字c#也不应该(开玩笑;))

严肃地说:内部关系不如朋友关系好,但它确实能完成工作。请记住,您很少会不通过DLL将代码分发给第三方开发人员;因此,只要您和您的团队了解内部类及其使用,就应该没问题。

让我来澄清一下friend关键字是如何破坏OOP的。

私有和受保护的变量和方法可能是OOP中最重要的部分之一。对象可以保存只有它们可以使用的数据或逻辑的思想允许您编写独立于环境的功能实现—并且您的环境不能更改它不适合处理的状态信息。通过使用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();
    }
}

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

如果你正在使用c++,你发现你自己使用friend关键字,这是一个非常强烈的迹象,你有一个设计问题,因为为什么一个类需要访问其他类的私有成员?

这实际上不是c#的问题。这是IL中的一个基本限制。c#也受此限制,就像任何其他寻求可验证的。net语言一样。这个限制也包括c++ /CLI中定义的托管类(Spec第20.5节)。

话虽如此,我认为纳尔逊对为什么这是一件坏事有一个很好的解释。

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