Domanda

La parola chiave dell'amico C ++ consente un classe A designare classe B come suo amico. Ciò consente a Classe B di accedere ai membri privati ?? / protetti di classe A .

Non ho mai letto nulla sul motivo per cui questo è stato lasciato fuori da C # (e VB.NET). La maggior parte delle risposte a questa precedente domanda StackOverflow sembrano dire che è un parte utile di C ++ e ci sono buoni motivi per usarlo. Nella mia esperienza dovrei essere d'accordo.

Un'altra domanda mi sembra davvero chiedere come fare qualcosa di simile a amico in un'applicazione C #. Mentre le risposte generalmente ruotano attorno alle classi nidificate, non sembra elegante come usare la parola chiave friend .

L'originale Book Design Patterns lo utilizza regolarmente nei suoi esempi.

Quindi, in sintesi, perché manca amico da C # e qual è la "migliore pratica" modo (o modi) di simularlo in C #?

(A proposito, la parola chiave internal è non la stessa cosa, consente a tutte le classi dell'intero assembly di accedere a membri interni, mentre amico ti consente di dare una certa classe accesso completo a esattamente una altra classe)

È stato utile?

Soluzione

Avere amici nella programmazione è più o meno considerato "sporco". e facile da abusare. Rompe le relazioni tra le classi e mina alcuni attributi fondamentali di un linguaggio OO.

Detto questo, è una bella funzionalità e l'ho usata molte volte in C ++; e vorrei usarlo anche in C #. Ma scommetto a causa del "puro" di C # OOness (rispetto alla pseudo OOness di C ++) MS ha deciso che, poiché Java non ha una parola chiave amico C #, non dovrebbe neanche (solo scherzando;))

Una nota seria: interno non è buono come un amico ma fa il lavoro. Ricorda che è raro che distribuirai il tuo codice a sviluppatori di terze parti non tramite una DLL; quindi, fintanto che tu e il tuo team siete a conoscenza delle classi interne e del loro utilizzo, dovreste andare bene

MODIFICA Vorrei chiarire in che modo la parola chiave friend compromette OOP.

Variabili e metodi privati ??e protetti sono forse una delle parti più importanti di OOP. L'idea che gli oggetti possano contenere dati o logiche che solo loro possono utilizzare consente di scrivere l'implementazione della funzionalità indipendentemente dal proprio ambiente e che l'ambiente non può modificare le informazioni sullo stato che non è adatto a gestire. Usando amico stai accoppiando le implementazioni di due classi insieme - il che è molto peggio che se hai appena accoppiato la loro interfaccia.

Altri suggerimenti

Su una nota a margine. Usare l'amico non significa violare l'incapsulamento, ma al contrario si tratta di applicarlo. Come accessori + mutatori, sovraccarico degli operatori, eredità pubblica, downcast, ecc. , è spesso usato in modo improprio, ma non significa che la parola chiave non abbia, o peggio, uno scopo negativo.

Vedi Konrad Rudolph di message nell'altra discussione, o se preferisci vedere la voce pertinente nelle FAQ C ++.

Per informazioni, un'altra cosa correlata ma non abbastanza uguale in .NET è [InternalsVisibleTo] , che consente a un assembly di designare un altro assembly (come un assembly di unit test) che (effettivamente) ha " interno " accesso a tipi / membri nell'assembly originale.

Dovresti essere in grado di realizzare lo stesso tipo di cose che "amico" è usato per in C ++ usando le interfacce in C #. Richiede di definire esplicitamente quali membri vengono passati tra le due classi, il che è un lavoro extra ma può anche rendere il codice più facile da capire.

Se qualcuno ha un esempio di un uso ragionevole di " amico " che non può essere simulato utilizzando le interfacce, per favore condividilo! Mi piacerebbe capire meglio le differenze tra C ++ e C #.

Con amico un designer C ++ ha un controllo preciso su chi sono esposti i membri privati ??*. Ma è costretto a esporre tutti i membri privati.

Con internal un designer C # ha un controllo preciso sull'insieme dei membri privati ??che sta esponendo. Ovviamente, può esporre solo un singolo membro privato. Ma verrà esposto a tutte le classi nell'assemblea.

In genere, un designer desidera esporre solo alcuni metodi privati ??a poche altre classi selezionate. Ad esempio, in un modello di fabbrica di classe può essere desiderato che la classe C1 sia istanziata solo dalla fabbrica di classe CF1. Pertanto la classe C1 può avere un costruttore protetto e una fabbrica di classe amici CF1.

Come puoi vedere, abbiamo 2 dimensioni lungo le quali l'incapsulamento può essere violato. friend lo viola lungo una dimensione, internal lo fa lungo l'altra. Qual è una violazione peggiore del concetto di incapsulamento? Difficile da dire. Ma sarebbe bello avere sia friend che internal disponibili. Inoltre, una buona aggiunta a questi due sarebbe il terzo tipo di parola chiave, che verrebbe utilizzata su base membro per membro (come internal ) e specifica la classe target (come friend ).

* Per brevità userò "privato" invece di " privato e / o protetto " ;.

- Nick

Puoi avvicinarti a C ++ " amico " con la parola chiave C # " internal " .

In effetti, C # dà la possibilità di ottenere lo stesso comportamento in modo OOP puro senza parole speciali: sono interfacce private.

Per quanto riguarda la domanda What è l'equivalente in C # di un amico? è stato contrassegnato come duplicato di questo articolo e nessuno propone una realizzazione davvero buona - mostrerò qui la risposta a entrambe le domande.

L'idea principale è stata presa da qui: Che cos'è un'interfaccia privata?

Diciamo che abbiamo bisogno di alcune classi che possano gestire istanze di altre classi e chiamare alcuni metodi speciali su di esse. Non vogliamo dare la possibilità di chiamare questo metodo ad altre classi. Questa è esattamente la stessa cosa che fa la parola chiave c ++ dell'amico nel mondo c ++.

Penso che un buon esempio nella pratica reale potrebbe essere il modello Full State Machine in cui alcuni controller aggiornano l'oggetto stato corrente e passano a un altro oggetto stato quando necessario.

Potresti:

  • Il modo più semplice e peggiore per rendere pubblico il metodo Update () - sperare tutti capiscono perché è male.
  • Il prossimo modo è di contrassegnarlo come interno. Va bene se metti il ??tuo classi in un altro assembly ma anche in ogni classe di quell'assembly potrebbe chiamare ciascun metodo interno.
  • Usa l'interfaccia privata / protetta - e ho seguito in questo modo.

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

Bene, che dire dell'eredità?

Dobbiamo usare la tecnica descritta in Da esplicito le implementazioni dei membri dell'interfaccia non possono essere dichiarate virtuali e contrassegnare IState come protetto per dare la possibilità di derivare anche dal 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()");
    }
}

E infine come testare l'ereditarietà del controller di classe: ControllerTest.cs

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

Spero di coprire tutti i casi e la mia risposta è stata utile.

Friend è estremamente utile durante la scrittura di unit test.

Sebbene ciò abbia un costo per inquinare leggermente la tua dichiarazione di classe, è anche un promemoria forzato dal compilatore di quali test potrebbero effettivamente interessare lo stato interno della classe.

Un idioma molto utile e pulito che ho trovato è quando ho lezioni di fabbrica, rendendole amiche degli oggetti che creano che hanno un costruttore protetto. Più specificamente, questo è stato quando ho avuto un'unica fabbrica responsabile della creazione di oggetti di rendering corrispondenti per gli oggetti del writer di report, il rendering in un determinato ambiente. In questo caso hai un solo punto di conoscenza sulla relazione tra le classi di chi scrive i report (cose come blocchi di immagini, bande di layout, intestazioni di pagina ecc.) E i loro corrispondenti oggetti di rendering.

A C # manca l'amico " amico " parola chiave per lo stesso motivo la sua distruzione deterministica mancante. Il cambiamento delle convenzioni fa sentire le persone intelligenti, come se i loro nuovi modi fossero superiori a quelli di qualcun altro. Si tratta di orgoglio.

Dire che "le classi di amici sono cattive" è miope come altre dichiarazioni non qualificate come " non usare gotos " o "Linux è migliore di Windows".

L'amico " amico " la parola chiave combinata con una classe proxy è un ottimo modo per esporre solo determinate parti di una classe ad altre classi specifiche. Una classe proxy può fungere da barriera affidabile contro tutte le altre classi. & Quot; " pubblico; non consente tale targeting e l'utilizzo di " protetto " ottenere l'effetto con l'ereditarietà è imbarazzante se in realtà non esiste alcun concetto "è un" rapporto.

Questo non è in realtà un problema con C #. È una limitazione fondamentale in IL. C # è limitato da questo, come qualsiasi altro linguaggio .Net che cerca di essere verificabile. Questa limitazione include anche le classi gestite definite in C ++ / CLI ( Spec sezione 20.5 ).

Detto questo, penso che Nelson abbia una buona spiegazione sul perché questa è una cosa negativa.

Smetti di inventare scuse per questa limitazione. amico è cattivo, ma interno è buono? sono la stessa cosa, solo quell'amico ti dà un controllo più preciso su chi è autorizzato ad accedere e chi non lo è.

Questo serve a far rispettare il paradigma dell'incapsulamento? quindi devi scrivere metodi di accesso e adesso cosa? come dovresti impedire a tutti (tranne i metodi di classe B) di chiamare questi metodi? non puoi, perché non puoi nemmeno controllarlo, a causa della mancanza di " amico " ;.

Nessun linguaggio di programmazione è perfetto. C # è una delle migliori lingue che abbia mai visto, ma inventare stupide scuse per le funzioni mancanti non aiuta nessuno. In C ++, mi manca il semplice sistema evento / delegato, reflection (+ de / serializzazione automatica) e foreach, ma in C # mi manca il sovraccarico dell'operatore (sì, continuate a dirmi che non ne avete bisogno), parametri predefiniti, una const che non può essere eluso, eredità multipla (sì, continuate a dirmi che non ne avete bisogno e che le interfacce erano una sostituzione sufficiente) e la possibilità di decidere di eliminare un'istanza dalla memoria (no, questo non è terribilmente male a meno che non siate un riparatore)

Esiste InternalsVisibleToAttribute da .Net 3 ma sospetto che l'hanno aggiunto solo per soddisfare gli assiemi dopo l'aumento dei test unitari. Non riesco a vedere molte altre ragioni per usarlo.

Funziona a livello di assieme ma fa il lavoro dove interno no; ovvero, dove si desidera distribuire un assembly ma si desidera che un altro assembly non distribuito abbia un accesso privilegiato ad esso.

Giustamente richiedono che l'assembly degli amici sia fortemente codificato per evitare che qualcuno crei un amico finto insieme all'assembly protetto.

Ho letto molti commenti intelligenti su " amico " parola chiave & amp; concordo su cosa sia utile, ma penso a cosa " interno " la parola chiave è meno utile, & amp; entrambi fanno ancora male per la pura programmazione OO.

Cosa abbiamo? (dicendo di " amico " Dico anche di " internal ")

  • sta usando " amico " rende il codice meno puro rispetto a oo?
  • sì;

  • non utilizza " amico " migliora il codice?

  • no, dobbiamo ancora stabilire delle relazioni private tra le classi, & amp; possiamo farlo solo se rompiamo il nostro meraviglioso incapsulamento, quindi non va bene, posso dire cosa è ancora più malvagio che usare "amico".

L'uso di friend crea alcuni problemi locali, non usarlo crea problemi agli utenti della libreria di codici.

la buona soluzione comune per il linguaggio di programmazione che vedo così:

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

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

Cosa ne pensi? Penso che sia il più comune & amp; pura soluzione orientata agli oggetti. Puoi aprire l'accesso a qualsiasi metodo tu scelga per qualsiasi classe desideri.

Sospetto che abbia qualcosa a che fare con il modello di compilazione C #: costruire IL JIT compilandolo in fase di esecuzione. vale a dire: lo stesso motivo per cui i generici C # sono fondamentalmente diversi dai generici C ++.

Se stai lavorando con C ++ e trovi te stesso usando la parola chiave friend, è un'indicazione molto forte, hai un problema di progettazione, perché perché diamine una classe deve accedere ai membri privati ??dell'altra classe ??

puoi mantenerlo privato e usare la riflessione per chiamare le funzioni. Test framework può farlo se gli chiedi di testare una funzione privata

Usavo regolarmente un amico e non penso che sia una violazione di OOP o un segno di un difetto di progettazione. Esistono diversi luoghi in cui è il mezzo più efficiente per raggiungere il fine corretto con la minima quantità di codice.

Un esempio concreto è quando si creano assiemi di interfacce che forniscono un'interfaccia di comunicazione ad altri software. Generalmente ci sono alcune classi di pesi massimi che gestiscono la complessità del protocollo e le peculiarità dei peer e forniscono un modello di connessione / lettura / scrittura / inoltro / disconnessione relativamente semplice che prevede il passaggio di messaggi e notifiche tra l'app client e l'assembly. Quei messaggi / notifiche devono essere racchiusi in classi. Gli attributi generalmente devono essere manipolati dal software del protocollo in quanto è il loro creatore, ma molte cose devono rimanere di sola lettura al mondo esterno.

È semplicemente sciocco dichiarare che si tratta di una violazione di OOP per il protocollo / "creatore" per avere un accesso intimo a tutte le classi create - la classe creatore ha dovuto sottrarre ogni bit di dati durante la salita. Quello che ho trovato più importante è ridurre al minimo tutte le righe extra di codice BS "OOP per OOP's Sake" modello di solito porta a. Gli spaghetti extra fanno solo più bug.

Le persone sanno che puoi applicare la parola chiave interna a livello di attributo, proprietà e metodo? Non è solo per la dichiarazione di classe di livello superiore (anche se la maggior parte degli esempi sembra dimostrarlo.)

Se hai una classe C ++ che utilizza la parola chiave friend e vuoi emularla in una classe C #: 1. dichiarare pubblico il codice categoria C # 2. dichiarare tutti gli attributi / proprietà / metodi che sono protetti in C ++ e quindi accessibili agli amici come interni in C # 3. creare proprietà di sola lettura per l'accesso pubblico a tutti gli attributi e proprietà interni

Sono d'accordo che non sia uguale al 100% come amico, e unit test è un esempio molto prezioso della necessità di qualcosa come amico (come il codice di registrazione dell'analizzatore di protocollo). Tuttavia interno fornisce l'esposizione alle classi in cui si desidera avere esposizione e [InternalVisibleTo ()] gestisce il resto - sembra che sia nato appositamente per unit test.

Per quanto riguarda l'amico "essere migliore perché puoi controllare esplicitamente a quali classi ha accesso " - cosa diamine stanno facendo un mucchio di sospette classi malvagie nella stessa assemblea? Partiziona le tue assemblee!

L'amicizia può essere simulata separando interfacce e implementazioni. L'idea è: " Richiedi un'istanza concreta ma limita l'accesso alla costruzione di quell'istanza " ;.

Ad esempio

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

Nonostante il fatto che ItsMeYourFriend () è solo pubblico la classe Friend può accedervi, dal momento che nessun altro può ottenere un'istanza concreta di Amico . Ha un costruttore privato, mentre il metodo factory New () restituisce un'interfaccia.

Vedi il mio articolo Amici e interni interfaccia i membri gratuitamente con la codifica delle interfacce per i dettagli.

Alcuni hanno suggerito che le cose possono sfuggire al controllo usando l'amico. Sono d'accordo, ma ciò non diminuisce la sua utilità. Non sono sicuro che un amico danneggi necessariamente il paradigma OO più che rendere pubblici tutti i membri della tua classe. Certamente la lingua ti permetterà di rendere pubblici tutti i tuoi membri, ma è un programmatore disciplinato che evita quel tipo di modello di progettazione. Allo stesso modo un programmatore disciplinato riserverebbe l'uso dell'amico per casi specifici in cui ha senso. Sento che l'interno espone troppo in alcuni casi. Perché esporre una classe o un metodo a tutto nell'assembly?

Ho una pagina ASP.NET che eredita la mia pagina base, che a sua volta eredita System.Web.UI.Page. In questa pagina ho un codice che gestisce la segnalazione degli errori dell'utente finale per l'applicazione in un metodo protetto

ReportError("Uh Oh!");

Ora ho un controllo utente contenuto nella pagina. Voglio che il controllo utente sia in grado di chiamare i metodi di segnalazione errori nella pagina.

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

Non può farlo se il metodo ReportError è protetto. Posso renderlo interno, ma è esposto a qualsiasi codice nell'assembly. Lo voglio solo esposto agli elementi dell'interfaccia utente che fanno parte della pagina corrente (inclusi i controlli figlio). Più specificamente, desidero che la mia classe di controllo di base definisca esattamente gli stessi metodi di segnalazione degli errori e che chiami semplicemente i metodi nella pagina di base.

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

Credo che qualcosa come un amico possa essere utile e implementato nella lingua senza rendere la lingua meno "OO". come, forse come attributi, in modo da poter avere classi o metodi amici di classi o metodi specifici, consentendo allo sviluppatore di fornire un accesso molto specifico. Forse qualcosa come ... (pseudo codice)

[Friend(B)]
class A {

    AMethod() { }

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

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

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

Nel caso del mio esempio precedente forse ho qualcosa di simile al seguente (si può sostenere la semantica, ma sto solo cercando di far passare l'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);
    }
}

A mio modo di vedere, il concetto di amico non ha più rischi che renderlo pubblico o creare metodi o proprietà pubblici per accedere ai membri. Semmai un amico consente un altro livello di granularità nell'accessibilità dei dati e consente di restringere tale accessibilità piuttosto che estenderla con quella interna o pubblica.

B.s.d.

È stato affermato che gli amici fanno male alla pura OOness. Che sono d'accordo.

È stato anche affermato che gli amici aiutano l'incapsulamento, che sono anche d'accordo.

Penso che l'amicizia dovrebbe essere aggiunta alla metodologia OO, ma non esattamente come in C ++. Vorrei avere alcuni campi / metodi a cui la mia classe di amici può accedere, ma NON vorrei che accedessero a TUTTI i miei campi / metodi. Come nella vita reale, permetterei ai miei amici di accedere al mio frigorifero personale ma non permetterei loro di accedere al mio conto bancario.

Uno può implementarlo come seguito

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

Ciò naturalmente genererà un avvertimento del compilatore e danneggerà l'intellisense. Ma farà il lavoro.

In una nota a margine, penso che un programmatore fidato dovrebbe fare l'unità di test senza accedere ai membri privati. questo è abbastanza fuori dal campo di applicazione, ma prova a leggere su TDD. tuttavia, se vuoi ancora farlo (avere amici come c ++) prova qualcosa del genere

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

quindi scrivi tutto il tuo codice senza definire UNIT_TESTING e quando vuoi fare il test unitario aggiungi #define UNIT_TESTING alla prima riga del file (e scrivi tutto il codice che fa il test unitario in #if UNIT_TESTING). Questo dovrebbe essere gestito con cura.

Dal momento che penso che il test unitario sia un cattivo esempio per l'uso degli amici, darei un esempio del perché penso che gli amici possano essere buoni. Supponiamo di avere un sistema di rottura (classe). Con l'uso, il sistema di rottura si consuma e deve essere rinnovato. Ora, vuoi che solo un meccanico autorizzato possa ripararlo. Per rendere l'esempio meno banale, direi che il meccanico avrebbe usato il suo cacciavite personale (privato) per sistemarlo. Ecco perché la classe del meccanico dovrebbe essere amica della rottura del sistema.

L'amicizia può anche essere simulata usando " agenti " - alcune classi interne. Prendi in considerazione il seguente esempio:

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

Potrebbe essere molto più semplice se utilizzato per l'accesso solo ai membri statici. I vantaggi di tale implementazione sono che tutti i tipi sono dichiarati nell'ambito interno delle classi di amicizia e, a differenza delle interfacce, consente l'accesso ai membri statici.

Risponderò solo a " Come " domanda.

Ci sono così tante risposte qui, tuttavia vorrei proporre una specie di "modello di progettazione" per ottenere quella funzione. Userò un meccanismo di linguaggio semplice, che include:

  • Interfacce
  • Classe nidificata

Ad esempio abbiamo 2 classi principali: Studente e Università. Lo studente ha GPA a cui solo l'università ha avuto accesso. Ecco il codice:

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();
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top