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

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

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

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

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

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


当前回答

我只回答“如何”的问题。

这里有很多答案,但我想提出一种“设计模式”来实现这一功能。我将使用简单的语言机制,包括:

接口 嵌套类

例如,我们有两个主要的类:学生和大学。学生的GPA只有大学才允许获得。代码如下:

public interface IStudentFriend
{
    Student Stu { get; set; }
    double GetGPS();
}

public class Student
{
    // this is private member that I expose to friend only
    double GPS { get; set; }
    public string Name { get; set; }

    PrivateData privateData;

    public Student(string name, double gps) => (GPS, Name, privateData) = (gps, name, new PrivateData(this);

    // No one can instantiate this class, but Student
    // Calling it is possible via the IStudentFriend interface
    class PrivateData : IStudentFriend
    {
        public Student Stu { get; set; }

        public PrivateData(Student stu) => Stu = stu;
        public double GetGPS() => Stu.GPS;
    }

    // This is how I "mark" who is Students "friend"
    public void RegisterFriend(University friend) => friend.Register(privateData);
}

public class University
{
    var studentsFriends = new List<IStudentFriend>();

    public void Register(IStudentFriend friendMethod) => studentsFriends.Add(friendMethod);

    public void PrintAllStudentsGPS()
    {
        foreach (var stu in studentsFriends)
            Console.WriteLine($"{stu.Stu.Name}: stu.GetGPS()");
    }
}

public static void Main(string[] args)
{
    var Technion = new University();
    var Alex     = new Student("Alex", 98);
    var Jo       = new Student("Jo", 91);

    Alex.RegisterFriend(Technion);
    Jo.RegisterFriend(Technion);
    Technion.PrintAllStudentsGPS();

    Console.ReadLine();
}

其他回答

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

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

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

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

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

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);
    }
}

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

通过使用c#中的接口,您应该能够完成在c++中使用“friend”的相同种类的事情。它要求您显式地定义在两个类之间传递的成员,这是额外的工作,但也可能使代码更容易理解。

如果有人有一个合理使用“朋友”的例子,不能用界面模拟,请分享!我想更好地理解c++和c#之间的区别。