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

C ++的friend关键字允许class A class B指定class B作为它的朋友。 这允许Class B访问Class Bprivate / protected成员。

我从来没有读过任何东西,为什么这是离开了C#(和VB.NET)。 这个早期的StackOverflow问题的大部分答案似乎是说这是一个有用的C + +的一部分,有很好的理由使用它。 根据我的经验,我不得不同意。

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

原始的devise模式书在其示例中经常使用它。

总而言之,为什么C#中的friend错过了,在C#中模拟它的“最佳实践”方法是什么?

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

有编程的朋友或多或less被认为是“脏的”,容易被滥用。 它打破了类之间的关系,破坏了面向对象语言的一些基本属性。

这就是说,这是一个很好的function,我已经在C ++中使用过很多次了。 并希望在C#中使用它。 但是我敢打赌,因为C#的“纯”OOness(与C ++的伪OOness相比)MS决定,因为Java没有朋友关键字C#不应该(只是在开玩笑))

一个严肃的说法是:内部不如朋友,但它确实完成了工作。 记住,你很less会将你的代码分发给第三方开发者,而不是通过一个DLL; 所以只要你和你的团队知道内部的课程和他们的使用,你应该没问题。

编辑让我澄清如何friend关键字破坏OOP。

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

在旁边注意。 使用朋友并不是要违反封装,而是要强制执行封装。 像accessor + mutators,运算符重载,公有inheritance,向下转换 ,经常被滥用,但并不意味着关键字没有或者更糟糕的目的。

请参阅Konrad Rudolph在其他主题中的消息 ,或者如果您希望在C ++ FAQ中查看相关条目 。

有关信息,.NET中的另一个相关但并非完全相同的东西是[InternalsVisibleTo] ,它允许组件指定另一个程序集(例如unit testing程序集)(有效地)对内部types具有“内部”访问/原始程序集中的成员。

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

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

friend ,C ++devise人员可以精确地控制私人*成员接触到的人。 但是,他被迫揭露了每一个私人成员。

通过internal ,C#devise人员可以精确控制他正在公开的一组私人成员。 显然,他只能暴露一个私人成员。 但是,它会暴露给大会的所有类。

通常情况下,devise师只希望向选定的几个其他类公开less数私有方法。 例如,在类工厂模式中,可能希望类C1仅由类工厂CF1实例化。 因此C1类可能有一个受保护的构造函数和一个朋友类工厂CF1。

正如你所看到的,我们有2个维度,封装可以被破坏。 friend一方面违背了它,另一方面是internal的。 哪一个是封装概念中更糟糕的违规? 很难说。 但是,有friendinternal可用,这将是很好的。 此外,对这两者的一个很好的补充将是第三种types的关键字,这个关键字将在成员的基础上(如internal )使用,并指定目标类(如friend )。

*为简洁起见,我将使用“私人”而不是“私人和/或受保护的”。

– 尼克

你可以用C#关键字“internal”来接近C ++的“friend ” 。

这实际上不是C#的问题。 这是IL的一个基本限制。 C#受到这个限制,就像任何其他.Net语言一样,都是可以validation的。 此限制还包括在C ++ / CLI中定义的托pipe类( 规范第20.5节 )。

这就是说,我认为尼尔森有一个很好的解释,为什么这是一件坏事。

编写unit testing时,朋友是非常有用的。

虽然这样做的代价是稍微污染了你的类声明,但是它也是一个编译器强制提醒的,testing实际上可能关心类的内部状态。

我发现了一个非常有用和干净的用法,就是当我有工厂类时,使它们成为他们创build的具有受保护的构造函数的项目的朋友。 更具体地说,这是当我有一个工厂负责为报表编写器对象创build匹配的渲染对象,渲染到给定的环境。 在这种情况下,您对报表编写器类(如图片块,布局带,页面标题等)与其匹配的呈现对象之间的关系具有单一的知识点。

由于缺less确定性破坏的原因,C#缺less“friend”关键字。 改变惯例会让人觉得自己很聪明,就好像他们的新方法优于别人的老方法一样。 这一切都是为了骄傲。

说“朋友class不好”与其他“不要用gotos”或“Linux比Windows好”这样的不合格语句是短视的。

将“friend”关键字与一个代理类相结合是一个很好的方法,只能将某个类的某些部分暴露给特定的其他类。 代理类可以作为对所有其他类的可信赖的屏障。 “公开”不允许任何这样的定位,而使用“受保护”来获得inheritance的效果是尴尬的,如果真的没有概念上的“是”的关系。

事实上,C#提供了以纯粹的OOP方式获得相同行为的可能性,而不需要特殊的词语 – 它是专用接口。

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

主要想法是从这里采取: 什么是私人界面?

比方说,我们需要一些可以pipe理其他类的实例的类,并调用一些特殊的方法。 我们不想给这个方法调用其他类。 这与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 StateBase(); //Controller.IState is hidden StateBase p = new StateBase(); //p.Update(); //is not accessible } } 

那么inheritance呢?

我们需要使用描述的技术因为明确的接口成员实现不能被声明为虚拟的,并且标记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()"); } } 

最后是如何testing类控制器抛出inheritance: ControllerTest.cs

 class ControllerTest: Controller { public ControllerTest() { IState testObj = new PlayerIdleState(); testObj.Update(); } } 

希望我涵盖所有案件,我的答案是有用的。

停止为这个限制找借口。 朋友是坏的,但内部是好的? 他们是一样的东西,只有那个朋友才能更精确地控制谁可以进入,谁不能进入。

这是为了强制封装范例? 所以你必须写accessor方法,现在呢? 你应该如何阻止每个人(除了B类的方法)调用这些方法? 你不能,因为你不能控制这个,因为失去了“朋友”。

没有编程语言是完美的。 C#是我见过的最好的语言之一,但为缺less的function做出愚蠢的借口并不能帮助任何人。 在C ++中,我错过了简单的事件/委托系统,reflection(+自动de /序列化)和foreach,但在C#中我错过了运算符重载(是的,不断告诉我,你不需要它),默认参数,const不能被规避,多重inheritance(是的,不断告诉我,你不需要它,接口是一个足够的替代),并决定从内存中删除一个实例的能力(不,这不是非常糟糕的,除非你是一个工匠)

从.Net 3开始,就有了InternalsVisibleToAttribute,但是我猜测他们只是在unit testing兴起之后才添加它来迎合testing程序集。 我看不到其他许多原因使用它。

它在组装层面上工作,但是在内部没有的情况下工作。 也就是说,您想要分发程序集的位置,但希望另一个非分布式程序集具有对其的特权访问权限。

相当正确的是,他们需要强大的朋友大会,以避免有人在你的受保护的大会旁边创造一个假装的朋友。

我已经阅读了许多有关“朋友”关键字的巧妙评论,我同意它是有用的东西,但我认为“内部”关键字是不太有用的,他们都仍然不利于纯粹的面向对象程序devise。

我们有什么? (说“朋友”我也在说“内部”)

  • 使用“朋友”使得代码对于oo不那么纯粹?
  • 是;

  • 是不是使用“朋友”使代码更好?

  • 不,我们还需要在class级之间build立一些私人的关系,只有打破美丽的封装,才能做到这一点,所以也不是好,我可以说比“朋友”更甚。

使用朋友会造成一些本地的问题,而不是使用它会造成代码库用户的问题。

编程语言的常见好的解决scheme我看到这样的:

 // c++ style class Foo { public_for Bar: void addBar(Bar *bar) { } public: private: protected: }; // c# class Foo { public_for Bar void addBar(Bar bar) { } } 

你怎么看待这件事? 我认为这是最常见和纯粹的面向对象的解决scheme。 你可以打开任何你想要的课程。

我怀疑它与C#编译模型有关 – 构buildIL在运行时的JIT编译。 即:与C ++generics基本上不同于C ++generics的原因相同。

如果您正在使用C ++,并且使用friend关键字find自己,那么这是一个非常明显的表示,您有一个devise问题,因为为什么类需要访问其他类的私有成员?

你可以保持私密,并使用reflection来调用函数。 testing框架可以做到这一点,如果你问它来testing一个私人function

我曾经经常使用朋友,我不认为这是违反面向对象或任何devise缺陷的迹象。 有几个地方最适合以最less量的代码实现最有效的手段。

一个具体的例子就是创build接口程序集,它提供了一些其他软件的通信接口。 一般来说,有几个重量级的类可以处理协议和对等特性的复杂性,并提供一个相对简单的连接/读/写/转发/断开模型,包括在客户端应用程序和程序集之间传递消息和通知。 这些消息/通知需要包装在类中。 这些属性通常需要由协议软件来操作,因为它是它们的创build者,但是很多东西只能保持只读的外部世界。

声明协议/“创build者”类对所有已创build类的亲密访问违反了OOP是很简单的事情 – 创build者类不得不在每一个数据的位置上填充数据。 我发现最重要的是最小化所有BS额外的代码行“OOP的清酒”模型通常导致。 额外的意大利面条只会造成更多的错误。

有人知道你可以在属性,属性和方法级别应用内部关键字吗? 这不仅仅是最高级的声明(尽pipe大多数例子似乎表明了这一点)。

如果您有一个使用friend关键字的C ++类,并且希望在C#类中模拟它:1.声明C#类public 2.声明所有在C ++中受保护的属性/属性/方法内部在C#中3.创build只读属性,以公共访问所有内部属性和属性

我同意这与朋友不是100%相同,unit testing是一个非常有价值的例子,需要像朋友(协议分析器日志代码)的东西。 然而内部提供了暴露给你想要暴露的类,[InternalVisibleTo()]处理其余部分 – 看起来像它是专门为unit testing而生的。

就朋友而言,“因为你可以明确地控制哪些类别可以访问,所以更好” – 在同一个程序集中,一群可疑的邪恶类别首先在做什么呢? 分区你的程序集!

友谊可以通过分离接口和实现来模拟。 这个想法是:“ 要求一个具体的实例,但限制该实例的build筑访问 ”。

例如

 interface IFriend { } class Friend : IFriend { public static IFriend New() { return new Friend(); } private Friend() { } private void CallTheBody() { var body = new Body(); body.ItsMeYourFriend(this); } } class Body { public void ItsMeYourFriend(Friend onlyAccess) { } } 

尽pipeItsMeYourFriend()是公开的,但只有Friend类可以访问它,因为没有其他人可能获得Friend类的具体实例。 它有一个私有的构造函数,而New()方法返回一个接口。

查看我的文章朋友和内部接口成员免费编码接口的细节。

有人build议使用朋友可能会失控。 我同意,但这并不减less它的用处。 我不确定,朋友必然会伤害OO范式,而不是让所有的class级成员公开。 当然,这种语言可以让你公开所有的成员,但它是一个有纪律的程序员,可以避免这种types的devise模式。 同样,一个有纪律的程序员会在有意义的特定情况下保留对朋友的使用。 在某些情况下,我觉得内部曝光太多了。 为什么要暴露一个类或方法到程序集中的所有东西?

我有一个ASP.NET页面,inheritance我自己的基本页面,继而inheritanceSystem.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); } 

我相信像朋友这样的东西在语言中可能是有用的和实现的,而不会使语言变得不那么“OO”,也许可以作为属性,以便可以让类或方法成为特定类或方法的朋友,从而允许开发者具体访问。 也许像…(伪代码)

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

正如我所看到的,朋友的概念没有比公开的方式更多的风险,或者创build公共方法或属性来访问成员。 如果有什么朋友允许在数据的可访问性的另一个级别的粒度,并允许您缩小可访问性,而不是扩大与内部或公共。

BSD

有人指出,朋友伤害纯粹的OOness。 我同意这一点。

还有人表示,朋友帮助封装,我也同意。

我认为友谊应该被添加到面向对象的方法论中,但不像在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); } } 

That will of course generate a compiler warning, and will hurt the intellisense. But it will do the work.

On a side note, I think that a confident programmer should do the testing unit without accessing the private members. this is quite out of the scope, but try to read about TDD. however, if you still want to do so (having c++ like friends) try something like

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

so you write all your code without defining UNIT_TESTING and when you want to do the unit testing you add #define UNIT_TESTING to the first line of the file(and write all the code that do the unit testing under #if UNIT_TESTING). That should be handled carefully.

Since I think that unit testing is a bad example for the use of friends, I'd give an example why I think friends can be good. Suppose you have a breaking system (class). With use, the breaking system get worn out and need to get renovated. Now, you want that only a licensed mechanic would fix it. To make the example less trivial I'd say that the mechanic would use his personal (private) screwdriver to fix it. That's why mechanic class should be friend of breakingSystem class.

The friendship may also be simulated by using "agents" – some inner classes. 考虑下面的例子:

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

It could be alot simpler when used for access only to static members. Benefits for such implementation is that all the types are declared in the inner scope of friendship classes and, unlike interfaces, it allows static members to be accessed.

Fields that ALL classes can access them are public .

Fields that NOT all other classes can access them are private .

(if the fields belongs to (declared inside) base class, then they are protected instead)

Fields that only their owner class can access them are private , and have no properties and get set methods.

Fields that only their owner class and some other classes can access them are private and each has special private get and set methods, and public share methods.

The some other classes that can also access these fields will have some private fields of delegate types and special public direct methods.

例:

 using System; class A { private int integer; //In the meantime, it seems that only A can access this integer private int GetInteger() //Get method is not public, because we don't want all other classes to use this integer { return this.integer; } private void SetInteger(int value) //Set method is not public, because we don't want all other classes to modify this integer { this.integer = value; } public void Share(ref B b) //I use the 'ref' keyword, to prevent the 'null' value in this argument, but if you call this method in a constructor of B, or in one of its methods, so you will have to remove the 'ref' keyword, or make overload of the same method without the 'ref' keyword, because 'ref this' is not allowed { b.DirectGetIntegerAndSetIntegerMethods(this.GetInteger, this.SetInteger); } public void PrintInteger() { Console.WriteLine(this.integer); } } class B //This class can access the 'integer' of A too, ie the 'integer' of A is "public" only for B { private Func<int> GetInteger; //Will execute the 'GetInteger' method of A private Action<int> SetInteger; //Will execute the 'SetInteger' method of A public void DirectGetIntegerAndSetIntegerMethods(Func<int> getInteger, Action<int> setInteger) { this.GetInteger = getInteger; this.SetInteger = setInteger; } public void Increment() { this.SetInteger(this.GetInteger() + 1); } } class Program { static void Main(string[] args) { A a = new A(); //Created new instance of A, and also new Int32 was initialized inside it and set to its default value 0, but unable to get or set its value, only just print it. a.PrintInteger(); B b = new B(); //Must create new instance of B, in order to change the integer of A to some value. For example, I will use b, to change the integer of a to 3 a.Share(ref b); //But before the change, I must tell 'a' to share his GetInteger and SetInteger methods to 'b', so 'b' will also be able execute them, through his Func<int> and Action<int> delegates, because GetInteger and SetInteger methods of A are private and cannot be executed directly. for (int i = 0; i < 3; i++) b.Increment(); a.PrintInteger(); //Print the integer of 'a' again after change. //Now the output of the console is: //0 //3 } } 

You have to know that using the 'friend' keyword of the C++ is to allow some classes to share their private members to some other classes directly .

Because of that the 'friend' keyword doesn't exist in C#, classes have no way to share their private fields to some other classes directly , but there is way to simulate it indeed as I shown above.

Do you know that the 'friend' keyword of C++ also can allow in the implementation of some functions to access the private members of some instances of some classes types?

The answer is yes and I will show you how to simulate this too in C#:

例:

 using System; using System.Reflection; //New namespace is added using System.Diagnostics; //Another new namespace is added too class Person { private readonly StackTrace st = new StackTrace(); //Helper object private readonly MethodInfo mi = typeof(Program).GetMethod("Foo"); //The MethodInfo of the method Foo, which will be declared and defined in class Program later, is the friend of the class Person //Both objects above are readonly, because they always reference the same objects that they were initialized with. private string name_of_his_dog; //Only Person can access this field private string GetTheNameOfHisDog() //Not public so that not all methods will be able to get this name { return this.name_of_his_dog; } private void SetTheNameOfHisDog(string new_name) //Not public so that not all methods will be able to set this name { this.name_of_his_dog = new_name; } public Func<string> ShareTheGetTheNameOfHisDogMethod() //Returns null, if the previous method that called this method is not friend of this class Person { if (this.st.GetFrame(1).GetMethod() == this.mi) return this.GetTheNameOfHisDog; return null; } public Action<string> ShareTheSetTheNameOfHisDogMethod() //Same as above { if (this.st.GetFrame(1).GetMethod() == this.mi) return this.SetTheNameOfHisDog; return null; } public void PrintTheNameOfHisDog() { Console.WriteLine(this.name_of_his_dog); } } class Program { static void Main(string[] args) { Person person = Foo(); person.PrintTheNameOfHisDog(); Func<string> getTheNameOfHisDog = person.ShareTheGetTheNameOfHisDogMethod(); //returns null, Main is not friend of Person Action<string> setTheNameOfHisDog = person.ShareTheSetTheNameOfHisDogMethod(); //returns null too, for the same reason setTheNameOfHisDog("Pointer"); //Runtime Error: Object reference not set to an instance of an object Console.WriteLine(getTheNameOfHisDog()); //Same runtime error //Output before runtime error: //Boxer //Boxer } public static Person Foo() //Only this method can get and set the name of the dog that belongs to the person { Person person = new Person(); Func<string> getTheNameOfHisDog = person.ShareTheGetTheNameOfHisDogMethod(); Action<string> setTheNameOfHisDog = person.ShareTheSetTheNameOfHisDogMethod(); setTheNameOfHisDog("Boxer"); Console.WriteLine(getTheNameOfHisDog()); return person; } } 

I must admit that before I posted this code, I didn't know how to find out the MethodInfo of the previous method that called the current method, but Firas Assaad's answer helped me, thanks to him too.

我怎样才能find调用当前方法的方法?

He suggested to use the System.Diagnostics.StackTrace class

Hope that you got my idea, and that helps and answers your question.

I didn't find this answer anywhere in the internet, I thought about this idea by myself using my brain.

I will answer only "How" question.

There are so many answers here, however I would like to propose kind of "design pattern" to achieve that feature. I will use simple language mechanism, which includes:

  • 接口
  • Nested class

For example we have 2 main classes: Student and University. Student has GPA which only university allowed to access. 这里是代码:

 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 = gps; Name = name; privateData = 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() { return Stu.GPS; } } // This is how I "mark" who is Students "friend" public void RegisterFriend(University friend) { friend.Register(privateData); } } public class University { List<IStudentFriend> 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()); } } } static void Main(string[] args) { University Technion = new University(); Student Alex = new Student("Alex", 98); Alex.RegisterFriend(Technion); Student Jo = new Student("Jo", 91); Jo.RegisterFriend(Technion); Technion.PrintAllStudentsGPS(); Console.ReadLine(); }