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

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

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

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

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

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


当前回答

我怀疑这与c#编译模型有关——在运行时构建IL和JIT编译。也就是说:c#泛型与c++泛型有本质区别的原因是一样的。

其他回答

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

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

Some have suggested that things can get out of control by using friend. I would agree, but that doesn't lessen its usefulness. I'm not certain that friend necessarily hurts the OO paradigm any more than making all your class members public. Certainly the language will allow you to make all your members public, but it is a disciplined programmer that avoids that type of design pattern. Likewise a disciplined programmer would reserve the use of friend for specific cases where it makes sense. I feel internal exposes too much in some cases. Why expose a class or method to everything in the assembly?

我有一个ASP。NET页面,它继承了我自己的基页,而基页又继承了System.Web.UI.Page。在本页中,我有一些代码,用于在受保护的方法中处理应用程序的最终用户错误报告

ReportError("Uh Oh!");

现在,我有了一个包含在页面中的用户控件。我希望用户控件能够调用页面中的错误报告方法。

MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");

如果ReportError方法是受保护的,它就不能这样做。我可以将它设置为内部的,但它会暴露给程序集中的任何代码。我只是想把它暴露给当前页面的一部分UI元素(包括子控件)。更具体地说,我希望我的基控件类定义完全相同的错误报告方法,并简单地调用基页中的方法。

protected void ReportError(string str) {
    MyBasePage bp = Page as MyBasePage;
    bp.ReportError(str);
}

我相信像friend这样的东西可以很有用,并且可以在语言中实现,而不会使语言变得不那么“面向对象”,比如作为属性,这样你就可以让类或方法成为特定类或方法的朋友,允许开发人员提供非常特定的访问。也许就像……(伪代码)

[Friend(B)]
class A {

    AMethod() { }

    [Friend(C)]
    ACMethod() { }
}

class B {
    BMethod() { A.AMethod() }
}

class C {
    CMethod() { A.ACMethod() }
}

在我之前的例子中,可能会有如下内容(有人可能会争论语义学,但我只是想让大家理解):

class BasePage {

    [Friend(BaseControl.ReportError(string)]
    protected void ReportError(string str) { }
}

class BaseControl {
    protected void ReportError(string str) {
        MyBasePage bp = Page as MyBasePage;
        bp.ReportError(str);
    }
}

在我看来,朋友概念并不比将事物公开,或创建公共方法或属性来访问成员更有风险。如果朋友允许数据可访问性的另一个粒度级别,并允许你缩小可访问性,而不是用内部或公共来扩大它。

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

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

不要再为这种限制找借口了。朋友是坏的,但内在是好的?它们是一样的东西,只是那个朋友让你更精确地控制谁可以访问谁不能访问。

这是强制封装范式吗?所以你必须写访问器方法,然后呢?你应该如何阻止所有人(除了类B的方法)调用这些方法?你不能,因为你也控制不了,因为你失去了“朋友”。

No programming language is perfect. C# is one of the best languages I've seen, but making silly excuses for missing features doesn't help anyone. In C++, I miss the easy event/delegate system, reflection (+automatic de/serialization) and foreach, but in C# I miss operator overloading (yeah, keep telling me that you didn't need it), default parameters, a const that cannot be circumvented, multiple inheritance (yeah, keep telling me that you didn't need it and interfaces were a sufficient replacement) and the ability to decide to delete an instance from memory (no, this is not horribly bad unless you are a tinkerer)