Pregunta

La palabra clave amigo de C ++ permite una clase A para designar a clase B como su amigo. Esto permite a Class B acceder a los private / protected de class A .

Nunca he leído nada sobre por qué se dejó esto fuera de C # (y VB.NET). La mayoría de las respuestas a esta pregunta anterior de StackOverflow parecen estar diciendo que es una Parte útil de C ++ y hay buenas razones para usarlo. En mi experiencia tendría que estar de acuerdo.

Otra pregunta me parece realmente una pregunta cómo hacer algo similar a friend en una aplicación de C #. Si bien las respuestas generalmente giran en torno a clases anidadas, no parece tan elegante como usar la palabra clave friend .

El libro de patrones de diseño lo usa regularmente a lo largo de sus ejemplos.

Entonces, en resumen, ¿por qué falta friend en C #, y cuál es la " mejor práctica " ¿Manera (o maneras) de simularlo en C #?

(Por cierto, la palabra clave interno es no lo mismo, permite que todas las clases dentro del ensamblaje completo accedan a miembros internos , mientras que friend le permite dar a una determinada clase acceso completo a exactamente una otra clase)

¿Fue útil?

Solución

Tener amigos en la programación se considera más o menos " sucio " Y fácil de abusar. Rompe las relaciones entre las clases y socava algunos atributos fundamentales de un lenguaje OO.

Dicho esto, es una buena característica y la he usado muchas veces en C ++; y me gustaría usarlo en C # también. Pero apuesto a que de C # " pure " OOness (en comparación con la pseudo OOness de C ++) MS decidió que, debido a que Java no tiene una palabra clave amiga, C # tampoco debería (solo es broma;))

En una nota seria: interno no es tan bueno como un amigo, pero hace el trabajo. Recuerde que es raro que distribuya su código a desarrolladores externos a través de una DLL; por lo tanto, siempre que usted y su equipo conozcan las clases internas y su uso, deberían estar bien.

EDITAR Permítame aclarar cómo la palabra clave de un amigo socava la POO.

Las variables y los métodos privados y protegidos son quizás una de las partes más importantes de la POO. La idea de que los objetos pueden contener datos o la lógica que solo ellos pueden usar le permite escribir su implementación de funcionalidad independiente de su entorno, y que su entorno no puede alterar la información de estado que no es adecuada para manejar. Al utilizar amigo, se están acoplando implementaciones de dos clases, lo que es mucho peor que si solo acoplás su interfaz.

Otros consejos

En una nota al margen. Usar a un amigo no se trata de violar la encapsulación, sino que, por el contrario, se trata de imponerla. Al igual que los accessors + mutators, la sobrecarga de operadores, la herencia pública, el downcasting, etc. , a menudo se usa incorrectamente, pero no significa que la palabra clave no tenga, o peor aún, un mal propósito.

Consulte Konrad Rudolph 's mensaje en el otro hilo, o si lo prefiere, vea la entrada relevante en las Preguntas frecuentes de C ++.

Para información, otra cosa relacionada, pero no exactamente igual en .NET es [InternalsVisibleTo] , que permite que un conjunto designe otro conjunto (como un conjunto de prueba de unidad) que (efectivamente) tiene " interno " acceso a tipos / miembros en el ensamblaje original.

Debes poder lograr el mismo tipo de cosas que " amigo " se usa para en C ++ usando interfaces en C #. Requiere que defina explícitamente qué miembros se están pasando entre las dos clases, lo que es un trabajo adicional, pero también puede hacer que el código sea más fácil de entender.

Si alguien tiene un ejemplo de uso razonable de " amigo " eso no puede ser simulado usando interfaces, por favor compártelo! Me gustaría entender mejor las diferencias entre C ++ y C #.

Con friend un diseñador de C ++ tiene un control preciso sobre a quién están expuestos los miembros privados *. Pero, se ve obligado a exponer a cada uno de los miembros privados.

Con interno un diseñador de C # tiene un control preciso sobre el conjunto de miembros privados que está exponiendo. Obviamente, él puede exponer a un solo miembro privado. Pero se expondrá a todas las clases en el ensamblaje.

Normalmente, un diseñador desea exponer solo algunos métodos privados a algunas otras clases seleccionadas. Por ejemplo, en un patrón de fábrica de clase puede desearse que la clase C1 sea instanciada solo por la clase de fábrica CF1. Por lo tanto, la clase C1 puede tener un constructor protegido y una fábrica de clase amiga CF1.

Como puede ver, tenemos 2 dimensiones a lo largo de las cuales se puede romper la encapsulación. friend lo rompe en una dimensión, internal lo hace en la otra. ¿Cuál es una brecha peor en el concepto de encapsulación? Difícil de decir. Pero sería bueno tener disponibles tanto friend como internal . Además, una buena adición a estos dos sería el tercer tipo de palabra clave, que se usaría miembro por miembro (como interno ) y especifica la clase objetivo (como friend ).

* Por brevedad, usaré " private " En lugar de " privado y / o protegido " ;.

- Nick

Puedes acercarte a C ++ " amigo " con la palabra clave C # " internal " .

De hecho, C # da la posibilidad de obtener el mismo comportamiento en modo OOP puro sin palabras especiales, son interfaces privadas.

En cuanto a la pregunta ¿Qué ¿El C # es equivalente a un amigo? se marcó como duplicado de este artículo y nadie propone una realización realmente buena; mostraré la respuesta en ambas preguntas aquí.

La idea principal se tomó desde aquí: ¿Qué es una interfaz privada?

Digamos, necesitamos alguna clase que pueda administrar instancias de otras clases y llamar a algunos métodos especiales en ellas. No queremos dar la posibilidad de llamar a estos métodos a otras clases. Esto es exactamente lo que hacen las palabras clave de c ++ de los amigos en c ++ world.

Creo que un buen ejemplo en la práctica real podría ser el patrón de Full State Machine en el que algunos controladores actualizan el objeto de estado actual y cambian a otro objeto de estado cuando sea necesario.

Podrías:

  • La forma más fácil y peor de hacer público el método Update () - esperanza todos entienden por qué es malo.
  • La siguiente forma es marcarlo como interno. Es lo suficientemente bueno si pones tu clases a otro conjunto, pero incluso entonces cada clase en ese conjunto Podría llamar a cada método interno.
  • Use la interfaz privada / protegida, y lo seguí de esta manera.

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

Bueno, ¿qué pasa con la herencia?

Necesitamos usar la técnica descrita en Desde explícito las implementaciones de los miembros de la interfaz no se pueden declarar virtuales y marcar IState como protegido para dar la posibilidad de derivar del Controlador también.

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

Y finalmente, ejemplo de cómo probar la herencia de lanzamiento de Controller: ControllerTest.cs

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

Espero que cubra todos los casos y mi respuesta fue útil.

Friend es extremadamente útil al escribir la prueba de unidad.

Si bien eso conlleva el costo de contaminar un poco su declaración de clase, también es un recordatorio forzado por el compilador de qué pruebas podrían realmente preocuparse por el estado interno de la clase.

Un lenguaje muy útil y limpio que he encontrado es cuando tengo clases de fábrica, haciéndoles amigos de los elementos que crean que tienen un constructor protegido. Más específicamente, esto fue cuando tuve una única fábrica responsable de crear objetos de representación coincidentes para los objetos del escritor de informes, la representación en un entorno determinado. En este caso, tiene un punto de conocimiento único sobre la relación entre las clases de informes y escritores (elementos como bloques de imágenes, bandas de diseño, encabezados de página, etc.) y sus objetos de representación coincidentes.

C # falta el " amigo " Palabra clave por la misma razón que falta la destrucción determinista. Cambiar las convenciones hace que las personas se sientan inteligentes, como si sus nuevas formas fueran superiores a las antiguas de otra persona. Se trata de orgullo.

Decir que " las clases de amigos son malas " es tan miope como otras declaraciones no calificadas, como " no usar gotos " o " Linux es mejor que Windows " ;.

El " amigo " la palabra clave combinada con una clase proxy es una excelente manera de exponer solo ciertas partes de una clase a otras clases específicas. Una clase proxy puede actuar como una barrera de confianza contra todas las demás clases. " público " no permite ninguna orientación de este tipo, y usar " protegido " obtener el efecto con la herencia es incómodo si realmente no hay un concepto conceptual "es un " relación.

Esto no es realmente un problema con C #. Es una limitación fundamental en IL. C # está limitado por esto, al igual que cualquier otro lenguaje .Net que busque ser verificable. Esta limitación también incluye clases administradas definidas en C ++ / CLI ( Spec). sección 20.5 ).

Dicho esto, creo que Nelson tiene una buena explicación de por qué esto es algo malo.

Deja de poner excusas para esta limitación. amigo es malo, pero interno es bueno? son lo mismo, solo que ese amigo te da un control más preciso sobre quién puede acceder y quién no.

¿Esto es para hacer cumplir el paradigma de encapsulación? ¿Así que tienes que escribir métodos de acceso y ahora qué? ¿Cómo se supone que debes evitar que todos (excepto los métodos de la clase B) llamen a estos métodos? no puedes, porque tampoco puedes controlar esto, porque falta " amigo " ;.

Ningún lenguaje de programación es perfecto. C # es uno de los mejores idiomas que he visto, pero hacer excusas tontas para las características que faltan no ayuda a nadie. En C ++, echo de menos el sistema fácil de delegado / evento, reflexión (+ des / serialización automática) y foreach, pero en C # echo de menos la sobrecarga de operadores (sí, continúe diciéndome que no la necesitaba), los parámetros predeterminados, una const. eso no puede ser evitado, herencia múltiple (sí, siga diciéndome que no la necesitaba y las interfaces fueron un reemplazo suficiente) y la capacidad de decidir eliminar una instancia de la memoria (no, esto no es terriblemente malo a menos que sea un tinkerer)

Existe el atributo InternalsVisibleToAttribute desde .Net 3, pero sospecho que solo lo agregaron para atender los ensamblajes de prueba después del aumento de las pruebas unitarias. No puedo ver muchas otras razones para usarlo.

Funciona a nivel de ensamblaje pero hace el trabajo donde interno no; es decir, donde desea distribuir un conjunto pero desea que otro conjunto no distribuido tenga acceso privilegiado a él.

Es cierto que requieren que la asamblea de amigos tenga una clave sólida para evitar que alguien cree un amigo ficticio junto a su asamblea protegida.

He leído muchos comentarios inteligentes sobre " amigo " palabra clave & amp; Estoy de acuerdo con lo que es útil, pero creo que lo que " interno " la palabra clave es menos útil, & amp; ambos siguen siendo malos para la programación OO pura.

¿Qué tenemos? (diciendo sobre " amigo " también digo sobre " interno ")

  • está utilizando " amigo " ¿Hace el código menos puro con respecto a oo?
  • sí ;

  • no está utilizando " amigo " hace el código mejor?

  • no, todavía necesitamos establecer algunas relaciones privadas entre clases, & amp; podemos hacerlo solo si rompemos nuestra hermosa encapsulación, por lo que tampoco es bueno, puedo decir lo que es aún más malo que usar "amigo".

El uso de friend crea algunos problemas locales, no usarlo crea problemas para los usuarios de la biblioteca de códigos.

la buena solución común para el lenguaje de programación que veo así:

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

// c#
class Foo {
    public_for Bar void addBar(Bar bar) { }
}

¿Qué piensas al respecto? Creo que es el más común y amp; Solución pura orientada a objetos. Puede abrir el acceso a cualquier método que elija a cualquier clase que desee.

Sospecho que tiene algo que ver con el modelo de compilación de C #: compilar IL el JIT compilando eso en tiempo de ejecución. es decir: la misma razón por la que los genéricos de C # son fundamentalmente diferentes a los genéricos de C ++.

Si está trabajando con C ++ y se encuentra usando una palabra clave de amigo, es una indicación muy clara de que tiene un problema de diseño, ¿por qué diablos necesita una clase acceder a los miembros privados de otra clase? / p>

puede mantenerlo privado y usar la función de reflexión para llamar a las funciones. El marco de prueba puede hacer esto si le pide que pruebe una función privada

Solía ??usar regularmente a un amigo, y no creo que sea una violación de la POO o una señal de algún defecto de diseño. Hay varios lugares donde es el medio más eficiente para el fin apropiado con la menor cantidad de código.

Un ejemplo concreto es cuando se crean conjuntos de interfaces que proporcionan una interfaz de comunicaciones a algún otro software. En general, hay algunas clases pesadas que manejan la complejidad del protocolo y las peculiaridades de los compañeros, y proporcionan un modelo relativamente simple de conexión / lectura / escritura / avance / desconexión que implica el paso de mensajes y notificaciones entre la aplicación cliente y el ensamblaje. Esos mensajes / notificaciones deben ser envueltos en clases. Los atributos generalmente deben ser manipulados por el software de protocolo, ya que es su creador, pero muchas cosas deben permanecer de solo lectura para el mundo exterior.

Es simplemente una tontería declarar que es una violación de la POO para el protocolo / "creador" La clase debe tener acceso íntimo a todas las clases creadas, la clase creadora ha tenido que muntear cada bit de datos en el camino hacia arriba. Lo que he encontrado más importante es minimizar todas las líneas adicionales de código de BS &OP; OOP for OOP's Sake " El modelo suele llevar a. Spaghetti extra solo hace más errores.

¿La gente sabe que puede aplicar la palabra clave interna en el nivel de atributo, propiedad y método? No es solo para la declaración de clase de nivel superior (aunque la mayoría de los ejemplos parecen mostrar eso).

Si tienes una clase de C ++ que usa la palabra clave friend y quieres emularla en una clase de C #: 1. declarar la clase C # pública 2. Declare todos los atributos / propiedades / métodos que están protegidos en C ++ y, por lo tanto, accesibles a los amigos como internos en C # 3. cree propiedades de solo lectura para el acceso público a todos los atributos y propiedades internos

Estoy de acuerdo en que no es 100% lo mismo que un amigo, y la prueba de unidad es un ejemplo muy valioso de la necesidad de algo así como un amigo (como es el código de registro del analizador de protocolo). Sin embargo, interno proporciona la exposición a las clases que desea tener, y [InternalVisibleTo ()] maneja el resto, parece que nació específicamente para una prueba de unidad.

En cuanto a que amigo " sea mejor porque puedes controlar explícitamente qué clases tienen acceso " ¿Qué diablos hacen un grupo de sospechosas clases malvadas en la misma asamblea en primer lugar? ¡Particiona tus ensamblajes!

La amistad se puede simular separando interfaces e implementaciones. La idea es: " Requerir una instancia concreta pero restringir el acceso a la construcción de esa instancia " ;.

Por ejemplo

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

A pesar del hecho de que ItsMeYourFriend () es público, solo la clase Friend puede acceder a ella, ya que nadie más puede obtener una instancia concreta de la Clase de amigo . Tiene un constructor privado, mientras que el método New () de fábrica devuelve una interfaz.

Vea mi artículo Friends and internal los miembros de la interfaz sin costo con la codificación de las interfaces para más detalles.

Algunos han sugerido que las cosas pueden salirse de control mediante el uso de un amigo. Estoy de acuerdo, pero eso no disminuye su utilidad. No estoy seguro de que el amigo necesariamente dañe el paradigma OO más que hacer público a todos los miembros de su clase. Ciertamente, el lenguaje le permitirá hacer públicos a todos sus miembros, pero es un programador disciplinado que evita ese tipo de patrón de diseño. Del mismo modo, un programador disciplinado reservaría el uso de un amigo para casos específicos en los que tenga sentido. Siento que las exposiciones internas demasiado en algunos casos. ¿Por qué exponer una clase o método a todo en el ensamblaje?

Tengo una página ASP.NET que hereda mi propia página base, que a su vez hereda System.Web.UI.Page. En esta página, tengo un código que maneja el informe de errores del usuario final para la aplicación en un método protegido

ReportError("Uh Oh!");

Ahora, tengo un control de usuario que está contenido en la página. Quiero que el control de usuario pueda llamar a los métodos de informe de errores en la página.

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

No se puede hacer eso si el método ReportError está protegido. Puedo hacerlo interno, pero está expuesto a cualquier código en el ensamblaje. Solo quiero que esté expuesto a los elementos de la interfaz de usuario que forman parte de la página actual (incluidos los controles secundarios). Más específicamente, quiero que mi clase de control base defina exactamente los mismos métodos de informe de errores, y simplemente llame a los métodos en la página base.

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

Creo que algo como un amigo podría ser útil e implementado en el idioma sin hacer que el idioma sea menos " OO " Me gusta, tal vez como atributos, para que puedas tener clases o métodos que sean amigos de clases o métodos específicos, permitiendo al desarrollador proporcionar un acceso muy específico. Quizás algo como ... (pseudo código)

[Friend(B)]
class A {

    AMethod() { }

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

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

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

En el caso de mi ejemplo anterior, quizás tenga algo como lo siguiente (se puede argumentar la semántica, pero solo estoy tratando de hacer entender la idea):

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

Tal como lo veo, el concepto de amigo no tiene más riesgo que hacer cosas públicas, o crear métodos o propiedades públicas para acceder a los miembros. Si algo, un amigo permite otro nivel de granularidad en la accesibilidad de los datos y le permite limitar esa accesibilidad en lugar de ampliarla con información interna o pública.

B.s.d.

Se dijo que, los amigos lastiman la pureza pura. Que estoy de acuerdo.

También se dijo que los amigos ayudan a la encapsulación, lo cual también estoy de acuerdo.

Creo que la amistad debería agregarse a la metodología OO, pero no como en C ++. Me gustaría tener algunos campos / métodos a los que pueda acceder mi clase de amigos, pero NO me gustaría que accedan a TODOS mis campos / métodos. Como en la vida real, dejé que mis amigos accedieran a mi refrigerador personal pero no les permití que accedieran a mi cuenta bancaria.

Uno puede implementar eso como sigue

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

Eso, por supuesto, generará una advertencia del compilador, y dañará el intellisense. Pero hará el trabajo.

En una nota lateral, creo que un programador seguro debe hacer la unidad de prueba sin acceder a los miembros privados. Esto está bastante fuera del alcance, pero trate de leer sobre TDD. sin embargo, si aún desea hacerlo (tener a c ++ como amigos) intente algo como

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

así que escribes todo tu código sin definir UNIT_TESTING y cuando quieres hacer la prueba de la unidad, agregas #define UNIT_TESTING a la primera línea del archivo (y escribes todo el código que hace la prueba de la unidad bajo #if UNIT_TESTING). Eso debe ser manejado con cuidado.

Como creo que las pruebas de unidad son un mal ejemplo para el uso de amigos, daré un ejemplo de por qué creo que los amigos pueden ser buenos. Supongamos que tienes un sistema de ruptura (clase). Con el uso, el sistema de rotura se desgasta y necesita ser renovado. Ahora, quieres que solo un mecánico con licencia lo arregle. Para hacer que el ejemplo sea menos trivial, diría que el mecánico usaría su destornillador personal (privado) para arreglarlo. Es por eso que la clase mecánica debe ser amiga de la clase breakSystem.

La amistad también puede simularse mediante el uso de " agentes " - Algunas clases internas. Considere el siguiente ejemplo:

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

Podría ser mucho más simple cuando se usa para acceder solo a miembros estáticos. Los beneficios de dicha implementación son que todos los tipos se declaran en el ámbito interno de las clases de amistad y, a diferencia de las interfaces, permite el acceso a miembros estáticos.

Responderé solo " Cómo " pregunta.

Hay tantas respuestas aquí, sin embargo, me gustaría proponer el tipo de "patrón de diseño". para lograr esa característica. Usaré un mecanismo de lenguaje simple, que incluye:

  • Interfaces
  • clase anidada

Por ejemplo, tenemos 2 clases principales: Estudiante y Universidad. El estudiante tiene un GPA al que solo la universidad puede acceder. Aquí está el código:

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(<*>quot;{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();
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top