当前位置:  开发笔记 > 编程语言 > 正文

为什么C#不提供C++风格的"朋友"关键字?

如何解决《为什么C#不提供C++风格的"朋友"关键字?》经验,为你挑选了10个好方法。

在C++朋友关键字允许class A指定class B作为其朋友.这允许Class B访问private/的protected成员class A.

我从来没有读过任何关于为什么这个被排除在C#(和VB.NET)之外的东西.早期StackOverflow问题的大多数答案似乎都说它是C++的一个有用部分,并且有充分的理由使用它.根据我的经验,我必须同意.

在我看来,另一个问题是如何friend在C#应用程序中执行类似的操作.虽然答案通常围绕嵌套类,但它看起来并不像使用friend关键字那么优雅.

最初的设计模式书在其示例中定期使用它.

总而言之,为什么friend缺少C#,以及在C#中模拟它的"最佳实践"方式(或方法)是什么?

(顺便说一句,该internal关键字一样的东西,它可以让所有的整个组件来访问内部的阶级internal的成员,同时friend允许你给某一类的完全访问权限,以正好一个其他类)



1> Luc Hermitte..:

在旁注.使用朋友不是违反封装,而是相反,它是关于强制执行它.与访问者+变更器,运算符重载,公共继承,向下转换等一样,它经常被误用,但这并不意味着关键字没有或者更糟糕的是坏的目的.

请参阅另一个主题中的Konrad Rudolph的消息,或者如果您愿意,请参阅C++ FAQ中的相关条目.


"*使用朋友不是违反封装,而是相反,它是关于强制执行*"
封装是关于强制执行不变量.当我们将一个类定义为另一个类的朋友时,我们明确地说这两个类与第一个类的不变量的管理密切相关.让班级成为朋友仍然比直接暴露所有属性(作为公共领域)或通过设置者更好.

2> 小智..:

让编程中的朋友或多或少地被认为是"肮脏的"并且容易被滥用.它打破了类之间的关系,破坏了OO语言的一些基本属性.

话虽如此,这是一个很好的功能,我自己在C++中使用了很多次; 并且也希望在C#中使用它.但我打赌因为C#的"纯粹"OOness(与C++的伪OOness相比)MS决定因为Java没有朋友关键字C#也不应该(开玩笑;))

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

编辑让我澄清一下friend关键字如何破坏OOP.

私有和受保护的变量和方法可能是OOP最重要的部分之一.对象可以保存只有他们可以使用的数据或逻辑的想法允许您编写独立于您的环境的功能实现 - 并且您的环境不能改变它不适合处理的状态信息.通过使用朋友,您将两个类的实现耦合在一起 - 如果您只是将它们的接口耦合在一起则更糟糕.


C#'internal'实际上比C++'朋友'更容易损害封装.通过"友谊",所有者明确地允许任何想要的人."内部"是一个开放式的概念.
这没有意义.`friend`不允许任意类或函数访问私有成员.它允许标记为朋友的特定功能或类这样做.拥有私有成员的类仍然可以100%控制对它的访问.你不妨争论公共成员的方法.两者都确保类中特定列出的方法可以访问类的私有成员.
安德斯应该因他遗漏的特征而不是他所包含的特征而受到称赞.
我不同意C#的内部伤害包裹.我认为相反.您可以使用它在程序集中创建封装的生态系统.许多对象可以共享此生态系统中未暴露给应用程序其余部分的内部类型.我使用"朋友"组装技巧为这些对象创建单元测试程序集.
我认为恰恰相反.如果程序集有50个类,如果其中3个类使用某个实用程序类,那么今天,您唯一的选择是将该实用程序类标记为"内部".这样,您不仅可以将它用于3个类,还可以用于程序集中的其余类.如果您想要做的就是提供更细粒度的访问,以便只有三个类可以访问实用程序类.它实际上使它更加面向对象,因为它改善了封装.
我不认为朋友因"OO纯度"而被排除在外.我认为这更有可能是因为在编译时C#被翻译为IL,并且您可能想要其他类型的朋友可用.这与C++泛型完全编译时的方式相同,而C#则不是.
不,这个答案实际上是错误的,SO上的其他线程已经解释了原因,例如http://stackoverflow.com/questions/17434/when-should-you-use-friend-in-c/17505#17505
@Nemanja Trifunovic:"内部"是一个开放式的概念.并不是的.由于程序员控制程序集中的类,因此您可以隐式控制哪些类可以访问它.但是,让类无论如何都可以直接访问你的东西是个坏主意,这就是.NET引入属性语法的原因.
朋友需要实现一些最基本的设计模式.通常,当需要多个类一起工作时需要它,例如管理器/托管模式.缺乏朋友让我经常使用像私人公开的黑客.

3> Marc Gravell..:

有关信息,.NET中的另一个相关但不完全相同的东西是[InternalsVisibleTo],它允许程序集指定另一个程序集(例如单元测试程序集),它(有效地)具有对内部类型/成员的"内部"访问权限.原装配.


很好的提示,因为寻找朋友的人通常希望找到一种方法来进行单元测试,并具备更多内部"知识"的能力.

4> Parappa..:

您应该能够通过使用C#中的接口来完成C++中使用"朋友"的相同类型的事物.它要求您明确定义在两个类之间传递的成员,这是额外的工作,但也可以使代码更容易理解.

如果某人有合理使用无法使用接口模拟的"朋友"的例子,请分享!我想更好地理解C++和C#之间的差异.


替换'Friend'*的接口方法*的问题是对象公开接口,这意味着任何人都可以将对象强制转换为该接口并且可以访问这些方法!将接口限定为内部接口,受保护接口或私有接口不起作用,就好像它可以工作一样,你可以只讨论有问题的方法!想想购物中心里的母亲和孩子.公众是世界上的每个人.内部只是商场里的人.私人只是母亲或孩子."朋友"只是母女之间的事情.

5> Nick Alexeev..:

使用friendC++设计器可以精确控制私有*成员所接触的对象.但是,他被迫揭露每一个私人成员.

使用internalC#设计师可以精确控制他所暴露的私人成员.显然,他只能暴露一个私人成员.但是,它将暴露给程序集中的所有类.

通常,设计者希望仅将少数私有方法暴露给选定的其他几个类.例如,在类工厂模式中,可能希望类C1仅由类工厂CF1实例化.因此,类C1可以具有受保护的构造函数和朋友类工厂CF1.

如您所见,我们有2个维度可以破坏封装. friend沿着一个维度突破它,沿着另一个维度internal.在封装概念中哪一个更糟糕?很难说.但是,拥有这两个friend并且internal可用是很好的.此外,对这两者的一个很好的补充是第三种类型的关键字,它将在逐个成员的基础上使用(如internal)并指定目标类(如friend).

*为简洁起见,我将使用"私人"而非"私人和/或受保护".

- 尼克



6> 小智..:

实际上,C#提供了以纯OOP方式获得相同行为的可能性而没有特殊的单词 - 它是私有接口.

至于问题什么是C#相当于朋友?被标记为与本文重复,没有人提出真正好的实现 - 我将在这里显示两个问题的答案.

主要想法是从这里开始:什么是私人接口?

比方说,我们需要一些可以管理另一个类的实例并在其上调用一些特殊方法的类.我们不希望将此方法调用到任何其他类.这与朋友c ++关键字在c ++世界中所做的完全相同.

我认为在实际操作中的好例子可能是Full State Machine模式,其中一些控制器更新当前状态对象并在必要时切换到另一个状态对象.

你可以:

使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 StateBase();
        //Controller.IState is hidden
        StateBase p = new 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();
    }
}

希望我涵盖所有案例,我的答案很有用.



7> jeffm..:

您可以使用C#关键字"internal"接近C++"friend ".


关闭,但不完全在那里.我想这取决于你如何构建你的程序集/类,但是最小化使用friend访问的类的数量会更优雅.在实用程序/帮助程序集中的示例中,并非所有类都应该具有对内部成员的访问权限.
@Ash:你需要习惯它,但是一旦你看到程序集作为应用程序的基本构建块,`internal`就会更有意义,并且在封装和实用程序之间提供了很好的平衡.不过,Java的默认访问权限更好.

8> Andy Dent..:

在编写单元测试时,朋友非常有用.

虽然这是以略微污染您的类声明为代价的,但它也是编译器强制提醒的,这些测试实际上可能关心类的内部状态.

我发现一个非常有用和干净的习惯是当我有工厂课程时,让他们成为他们创建的具有受保护构造函数的项目的朋友.更具体地说,当我有一个工厂负责为报表编写器对象创建匹配的渲染对象,渲染到给定的环境时.在这种情况下,您只需要了解报表编写器类(图像块,布局带,页眉等)与其匹配的渲染对象之间的关系.



9> 小智..:

C#缺少"friend"关键字,原因与其缺失的确定性破坏相同.变化的惯例使人们感到聪明,好像他们的新方式优于其他人的旧方式.一切都与骄傲有关.

说"朋友类不好"就像其他不合格的语句一样短视,比如"不要使用gotos"或"Linux比Windows更好".

"friend"关键字与代理类相结合是一种很好的方式,只将类的某些部分暴露给特定的其他类.代理类可以充当对所有其他类的可信障碍."public"不允许任何此类定位,并且如果确实没有概念上的"是"关系,则使用"protected"来获得继承效果会很尴尬.



10> JaredPar..:

这实际上不是C#的问题.这是IL的一个基本限制.C#受此限制,任何其他寻求可验证的.Net语言也是如此.此限制还包括在C++/CLI中定义的托管类(规范部分20.5).

话虽如此,我认为尼尔森有一个很好的解释,为什么这是一件坏事.


-1."朋友"不是坏事; 相反,它远比"内部"好.纳尔逊的解释很糟糕而且不正确.
推荐阅读
echo7111436
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有