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#之间的区别。

其他回答

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

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

B.s.d。

有人说,朋友伤害纯粹的孤独。我同意这一点。

还有人说,朋友会帮助封装,我也同意这一点。

我认为友谊应该添加到OO方法中,但不像在c++中那样。我想有一些字段/方法,我的朋友类可以访问,但我不希望他们访问我所有的字段/方法。在现实生活中,我会让我的朋友们进入我的私人冰箱,但我不会让他们进入我的银行账户。

我们可以按照下面的方法实现它

    class C1
    {
        private void MyMethod(double x, int i)
        {
            // some code
        }
        // the friend class would be able to call myMethod
        public void MyMethod(FriendClass F, double x, int i)
        {
            this.MyMethod(x, i);
        }
        //my friend class wouldn't have access to this method 
        private void MyVeryPrivateMethod(string s)
        {
            // some code
        }
    }
    class FriendClass
    {
        public void SomeMethod()
        {
            C1 c = new C1();
            c.MyMethod(this, 5.5, 3);
        }
    }

这当然会生成一个编译器警告,并将损害智能感知。但它会起作用的。

另一方面,我认为一个自信的程序员应该在不访问私有成员的情况下进行测试单元。这已经超出了本文的范围,但请尝试阅读TDD。 然而,如果你仍然想这样做(有c++喜欢的朋友),尝试一些像

#if UNIT_TESTING
        public
#else
        private
#endif
            double x;

所以你写的所有代码都没有定义UNIT_TESTING,当你想要进行单元测试时,你在文件的第一行添加#define UNIT_TESTING(并在#if UNIT_TESTING下编写所有进行单元测试的代码)。这应该小心处理。

因为我认为单元测试对于朋友的使用是一个不好的例子,所以我想举一个例子来说明为什么我认为朋友可以是好的。假设你有一个破碎的系统(类)。随着使用,破碎系统磨损,需要翻新。现在,你希望只有有执照的技工才能修理它。为了让这个例子不那么琐碎,我想说机械师会用他的私人螺丝刀来修理它。这就是为什么机制类应该是breakingSystem类的朋友。

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

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.
  }
}

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

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

Friend在编写单元测试时非常有用。

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

A very useful and clean idiom I've found is when I have factory classes, making them friends of the items they create which have a protected constructor. More specifically, this was when I had a single factory responsible for creating matching rendering objects for report writer objects, rendering to a given environment. In this case you have a single point of knowledge about the relationship between the report-writer classes (things like picture blocks, layout bands, page headers etc.) and their matching rendering objects.