Pergunta

O C ++ amigo chave permite uma class A para class B designará como seu amigo. Isso permite que Class B para acessar os membros private / protected de class A.

Eu nunca li nada a respeito de porque isso foi deixado de fora do C # (e VB.NET). A maioria das respostas para essa anteriormente questão StackOverflow parecem estar dizendo que é uma parte útil do C ++ e há boas razões para usá-lo. Na minha experiência, eu tenho que concordar.

Outra questão me parece ser realmente perguntando como fazer algo semelhante ao friend em uma aplicação C #. Enquanto as respostas geralmente giram em torno de aulas aninhadas, não parece tão elegante como utilizando palavras-chave friend.

O original Design Patterns livro usa -lo regularmente ao longo de seus exemplos.

Então, em resumo, por que é friend faltando C #, e qual é o caminho "melhor prática" (ou formas) de simular-lo em C #?

(A propósito, o internal palavra-chave não a mesma coisa, ele permite que todas classes dentro de toda a montagem aos membros acesso internal, enquanto friend permite que você dê uma certa classe acesso completo e exatamente um outra classe)

Foi útil?

Solução

Ter amigos na programação é mais ou menos considerado "sujo" e fácil de abuso. Ele rompe as relações entre as classes e prejudica alguns atributos fundamentais de uma linguagem OO.

Dito isto, é um bom recurso e eu usei-o muitas vezes me em C ++; e gostaria de usá-lo em C # também. Mas eu aposto que por causa de C # 's OOness "puro" (em comparação com C ++' s OOness pseudo) MS decidiu que porque Java não tem amigo palavra-chave C # não deve ou (apenas brincando;))

Em uma nota grave: interna não é tão boa como amigo, mas ele faz começar o trabalho feito. Lembre-se que é raro que você vai distribuir seu código para desenvolvedores de 3rd party não através de uma DLL; por isso, enquanto você e sua equipe saber sobre as classes internas e seu uso deve ser fino.

Editar Deixe-me esclarecer como a minar amigo chave OOP.

variáveis ??e métodos privados e protegidos são, talvez, uma das parte mais importante de OOP. A idéia de que os objetos podem armazenar dados ou lógica que só eles podem usar permite que você escreva sua implementação de funcionalidade independente do seu ambiente - e que seu ambiente pode informações de estado não alter que ele não é adequado para lidar. Usando amigo você está acoplando implementações duas classes' juntos - o que é muito pior do que se você apenas acoplada sua interface.

Outras dicas

Em uma nota lateral. Usando amigo não se trata de violar o encapsulamento, mas, pelo contrário trata-se de aplicá-la. Como assessores + Mutators, operadores de sobrecarga, herança público, downcasting, etc. , é muitas vezes mal utilizado, mas isso não significa que a palavra-chave não tem, ou pior, um propósito ruim.

Consulte Konrad Rudolph 's mensagem no outro segmento, ou se você preferir ver entrada relevante no ++ FAQ C.

Para informações, outra relacionada-mas-não-muito-a-mesma coisa em .NET é [InternalsVisibleTo], que permite uma montagem designar outra montagem (como um teste de unidade de montagem) que (efetivamente) tem acesso "interno" para tipos / membros na montagem original.

Você deve ser capaz de realizar os mesmos tipos de coisas que "amigo" é usado para em C ++ usando as interfaces em C #. Ela exige que você definir explicitamente que os membros estão sendo passados ??entre as duas classes, que é um trabalho extra, mas também pode tornar o código mais fácil de entender.

Se alguém tem um exemplo de um uso razoável de "amigo" que não pode ser simulada usando interfaces, por favor, compartilhe! Eu gostaria de entender melhor as diferenças entre C ++ e C #.

Com friend um C ++ designer tem controle preciso sobre quais os membros privados * estão expostos. Mas, ele é forçado a expor cada um dos membros privados.

Com internal um C # designer tem controle preciso sobre o conjunto de membros privados ele está expondo. Obviamente, ele pode expor apenas um único membro privado. Mas, ele vai ficar exposto a todas as classes na montagem.

Normalmente, um designer deseja expor apenas alguns métodos particulares para algumas outras classes selecionados. Por exemplo, num padrão de classe de fábrica, pode ser desejado que a classe C1 é instanciado apenas por classe fábrica CF1. Portanto classe C1 pode ter um construtor protegido e uma fábrica amigo classe CF1.

Como você pode ver, temos 2 dimensões ao longo do qual o encapsulamento pode ser violado. friend viola-lo ao longo de uma dimensão, internal faz isso ao longo do outro. Qual é uma violação pior no conceito de encapsulamento? Difícil de dizer. Mas seria bom ter tanto friend e internal disponível. Além disso, um bom complemento a estes dois seria o terceiro tipo de palavra-chave, que seria usado no elemento-a-elemento base (como internal) e especifica a classe alvo (como friend).
* Para abreviar vou usar "privado" em vez de "privada e / ou protegidos".
- Nick

Você pode chegar perto de C ++ "amigo" com o C # palavra-chave "interna ".

Na verdade, C # dá possibilidade de obter mesmo comportamento de forma OOP pura, sem palavras especiais. - É interfaces privadas

Quanto questão que ? é o C # equivalente do amigo foi marcado como duplicado a este artigo e ninguém lá propor realmente bom realização -. vou mostrar resposta em ambos pergunta aqui

A idéia principal foi tomar a partir daqui: O que é uma interface privada

Vamos dizer, precisamos de alguma classe que poderia gerenciar instâncias de outra classe e chamar alguns métodos especiais sobre eles. Nós não queremos dar possibilidade de chamar estes métodos para quaisquer outras classes. Este é exatamente o mesmo que o amigo c ++ palavra-chave fazer em c ++ mundo.

Eu acho bom exemplo na prática real pode ser padrão Máquina de Estado completa onde alguns atual objeto de estado de actualização do controlador e mudar para outro objeto de estado quando necessário.

Você pode:

  • A maneira mais fácil e pior para fazer Update () Método público - esperança todos a compreender por que é ruim.
  • Em seguida maneira é marcá-lo como interno. É bom o suficiente, se você colocar o seu aulas para outra montagem, mas mesmo assim cada classe em que a montagem poderia chamar cada método interno.
  • Use interface privada / protegida - e eu segui este caminho
  • .

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

Bem, e sobre herança?

Precisamos usar a técnica descrita no Desde explícita implementações de membro interface não pode ser declarada virtual e marca iState como protegido para dar possibilidade de derivar de controlador também.

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, finalmente exemplo de como teste de herança de classe controlador lance: ControllerTest.cs

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

Hope I cobrir todos os casos e minha resposta foi útil.

Amigo é extremamente útil quando se escreve teste de unidade.

Enquanto que vem a um custo de poluir sua declaração de classe ligeiramente, é também um lembrete imposta pelo compilador do que testa realmente pode se preocupam com o estado interno da classe.

A expressão muito útil e limpo que eu encontrei é quando eu tenho classes de fábrica, tornando-os amigos dos itens que eles criam que têm um construtor protegido. Mais especificamente, isso foi quando eu tinha uma única fábrica responsável pela criação de objetos de renderização de harmonização para objetos relatório escritor, tornando a um determinado ambiente. Neste caso, você tem um único ponto de conhecimentos sobre a relação entre as classes relatório-escritor (coisas como blocos de imagem, bandas de layout, cabeçalhos de página etc.) e seus objetos correspondentes renderização.

C # está faltando a palavra "amigo" pela mesma razão a sua falta destruição determinista. Alterar convenções faz as pessoas se sentir inteligente, como se as suas novas maneiras são superiores a alguém' velhas formas outra pessoa. É tudo uma questão de orgulho.

dizendo que "as classes amigos são ruins" é tão míope como outras declarações não qualificados como "não usar gotos" ou "Linux é melhor do que o Windows".

A palavra-chave "amigo" combinado com uma classe de proxy é uma ótima maneira de expor única certos partes de uma classe para outra classe específica (es). A classe de proxy pode agir como uma barreira confiável contra todas as outras classes. "Público" não permite qualquer segmentação, e usando "protegido" para obter o efeito com a herança é estranho se não há realmente nenhuma conceitual "é um" relacionamento.

Esta não é realmente um problema com C #. É uma limitação fundamental no IL. C # é limitado por isso, assim como qualquer outra linguagem .Net, que procura ser verificável. Esta limitação também inclui classes geridas definidos em C ++ / CLI ( Spec seção 20.5 ).

Dito isto eu acho que Nelson tem uma boa explicação de por que isso é uma coisa ruim.

parar de inventar desculpas para essa limitação. amigo é ruim, mas interno é bom? eles são a mesma coisa, só que o amigo lhe dá controle mais preciso sobre quem tem permissão para acesso e quem não é.

Esta é reforçar o paradigma encapsulamento? então você tem que escrever métodos de acesso e agora? como você deve parar todos (exceto os métodos de classe B) de chamar esses métodos? você não pode, porque você não pode controlar isso também, devido à falta de "amigo".

No linguagem de programação é perfeito. C # é uma das melhores linguagens que eu vi, mas fazendo desculpas tolas para características ausentes não ajuda ninguém. Em C ++, eu perca o sistema de eventos / delegado fácil, reflexão (+ de automático / serialização) e foreach, mas em C # Eu operador falta sobrecarga (sim, ficam me dizendo que você não precisa dele), os parâmetros padrão, um const que não podem ser contornadas, herança múltipla (sim, ficam me dizendo que você não precisa dele e interfaces eram um substituto suficiente) ea capacidade de decidir excluir uma instância da memória (não, isso não é terrivelmente ruim, a menos que você é um tinkerer)

Existe a InternalsVisibleToAttribute desde .Net 3, mas eu suspeito que eles só acrescentou que para atender a conjuntos de teste após a ascensão de testes de unidade. Eu não posso ver muitas outras razões para usá-lo.

Ele funciona no nível da montagem, mas ele faz o trabalho onde interna não; isto é, onde pretende distribuir uma montagem, mas quer outro não distribuídos montagem para ter acesso privilegiado a ele.

Justamente eles exigem o amigo de montagem a ser forte com chave para evitar que alguém criando um amigo fingir ao lado de seu protegido montagem.

Eu li muitos comentários inteligentes sobre "amigo" palavra-chave e eu concordo que é coisa útil, mas eu acho que o que palavra-chave "interno" é menos útil, e ambos ainda ruim para a programação OO puro.

O que nós temos? (Dizendo sobre "amigo" Eu também dizendo sobre "interno")

  • está usando "amigo" torna o código menos puro sobre a oo?
  • yes;

  • não está usando "amigo" faz código melhor?

  • Não, ainda precisamos fazer algumas relações privadas entre as classes, e podemos fazê-lo apenas se quebrar nossa bela encapsulamento, por isso também é bom isn`t, eu posso dizer o que ainda mais mal do que usar "amigo" .

Usando amigo faz com que alguns problemas locais, não usá-lo faz com que problemas para a biblioteca de códigos em usuários.

a solução bem comum para a linguagem de programação que eu vejo como isto:

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

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

O que você acha sobre isso? Eu acho que a solução mais comum e pura orientada a objetos. Você pode abrir o acesso de qualquer método que você escolher para qualquer classe que você deseja.

Eu suspeito que tem algo a ver com o modelo de compilação C # - construção de IL o JIT compilar que em tempo de execução. i.e .: pela mesma razão que o C # genéricos são fundamentalmente diferentes aos genéricos de C ++.

Se você está trabalhando com C ++ e você encontrar o seu próprio usando amigo palavra-chave, é uma indicação muito forte, que você tem um problema de design, porque por que diabos uma classe precisa acessar os membros privados de outra classe ??

Você pode mantê-lo reflexão privado e uso de funções de chamada. framework de teste pode fazer isso se você pedir para ele testar uma função privada

Eu costumava usar regularmente amigo, e eu não acho que é qualquer violação do OOP ou um sinal de qualquer falha de projeto. Há vários lugares onde é o meio mais eficiente para o fim adequado com a menor quantidade de código.

Um exemplo concreto é quando a criação de conjuntos de interface que fornecem uma comunicação interface para algum outro software. Geralmente existem algumas classes de pesos pesados ??que lidar com a complexidade dos protocolos e de pares peculiaridades, e fornecer gravação frente modelo / / desconexão envolvendo passando mensagens e notificações entre o aplicativo cliente e a montagem de um / connect relativamente simples leitura /. Essas mensagens / notificações precisam ser envolto em classes. Os atributos geralmente precisam ser manipulados pelo software de protocolo, pois é o seu criador, mas um monte de coisas tem que permanecer somente leitura para o mundo exterior.

É simplesmente idiota para declarar que é uma violação da OOP para o protocolo / "criador" de classe para ter acesso íntimo a todas as classes criadas - a classe criador teve de pouco munge cada bit de dados no caminho acima. O que eu encontrei mais importante é minimizar todas as linhas extras a BS de código "OOP para a causa da OOP" modelo geralmente leva a. spaghetti extra só faz mais erros.

As pessoas sabem que você pode aplicar a palavra-chave interno no atributo, propriedade e nível de método? Não é apenas para a declaração de classe de nível superior (embora a maioria dos exemplos parecem mostrar que.)

Se você tem uma classe C ++ que usa a palavra-chave amigo, e deseja emular que em uma classe C #: 1. Declarar o público classe C # 2. declarar todos os atributos / propriedades / métodos que são protegidos em C ++ e portanto acessível aos amigos como interno em C # 3. Criar ler apenas as propriedades de acesso público a todos os atributos internos e propriedades

Eu concordo que não é 100% o mesmo como amigo, e teste de unidade é um exemplo muito valiosa da necessidade de algo como amigo (como é o protocolo código de log analisador). No entanto interna fornece a orientação para as categorias que você quer ter a exposição e [InternalVisibleTo ()] cuida do resto -. Parece que ele nasceu especificamente para teste de unidade

Quanto amigo "ser melhor porque você pode explicitamente controlar quais classes têm acesso" - o que no heck são um bando de aulas más suspeitas fazendo no mesmo conjunto em primeiro lugar? Particionar seus conjuntos!

A amizade pode ser simulado por separar interfaces e implementações. A idéia é: " Exigir um exemplo concreto, mas restringir o acesso construção dessa instância "

Por exemplo

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

Apesar do fato de que ItsMeYourFriend() é de classe Friend única público pode acessá-lo, uma vez que ninguém pode, possivelmente, obter uma instância concreta da classe Friend. Tem um construtor privado, enquanto o método de fábrica New() retorna uma interface.

Veja meu artigo amigos e interna membros de interface, sem nenhum custo com codificação para interfaces de para mais detalhes.

Alguns sugeriram que as coisas podem ficar fora de controle usando amigo. Eu concordo, mas isso não diminui a sua utilidade. Eu não estou certo que o amigo dói necessariamente o paradigma OO mais do que fazer todo o seu membros da classe pública. Certamente a linguagem vai permitir que você faça todos os seus membros público, mas é um programador disciplinado que evite esse tipo de padrão de design. Da mesma forma um programador disciplinado reservaria o uso de amigo para casos específicos em que faz sentido. Sinto-me expõe internos demais em alguns casos. Por que expor uma classe ou método para tudo na montagem?

Eu tenho uma página ASP.NET que herda minha página própria base, que por sua vez herda System.Web.UI.Page. Nesta página, eu tenho algum código que o relatório de erros alças do usuário final para a aplicação em um método protegido

ReportError("Uh Oh!");

Agora, eu tenho um controle de usuário que está contido na página. Eu quero o controle de usuário para ser capaz de chamar os métodos de relatórios de erro na página.

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

Ele não pode fazer isso se o método ReportError está protegida. Eu posso fazer isso interno, mas ele é exposto a qualquer código na montagem. Eu só quero que exposta aos elementos de interface do usuário que fazem parte da página atual (incluindo controles filho). Mais especificamente, eu quero a minha classe de controle de base para definir exatamente o mesmo erro relato de métodos, e simplesmente chamar métodos na página de base.

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

Eu acredito que algo como amigo poderia ser útil e implementado na linguagem sem fazer a linguagem menos "OO", como, talvez como atributos, de modo que você pode ter aulas ou métodos de ser amigos para classes ou métodos específicos, permitindo que o desenvolvedor para fornecer acesso muito específico. Talvez algo como ... (código pseudo)

[Friend(B)]
class A {

    AMethod() { }

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

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

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

No caso do meu exemplo anterior, talvez, ter algo como o seguinte (pode-se discutir semântica, mas eu só estou tentando ficar com a ideia de diâmetro):

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 meu ver, o conceito amigo não tem mais risco para ele do que fazer as coisas público, ou a criação de métodos públicos ou propriedades para acessar membros. Se amigo nada permite um outro nível de granularidade na acessibilidade dos dados e permite restringir que a acessibilidade ao invés de ampliá-lo com interno ou público.

B.s.d.

Foi afirmado que, amigos dói OOness puro. A qual concordo.

Também foi afirmado que os amigos ajuda encapsulamento, que eu também concordo.

Eu acho que a amizade deve ser adicionado à metodologia OO, mas não tão-lo em C ++. Eu gostaria de ter alguns campos / métodos que meu amigo classe de acesso lata, mas eu não gosto deles para acessar todos os meus campos / métodos. Como na vida real, eu deixaria meus amigos acessar minha geladeira pessoal, mas eu não iria deixá-los para acessar minha conta bancária.

Pode-se implementar isso como seguido

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

Isso, naturalmente gerar um aviso do compilador, e vai machucar o intellisense. Mas ele vai fazer o trabalho.

Em uma nota lateral, eu acho que um programador confiante deve fazer o teste de unidade sem acessar os membros privados. isso é muito fora do escopo, mas tentar ler sobre TDD. No entanto, se você ainda quer fazer isso (ter c ++ como amigos) tentar algo como

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

para que você escreva todo o seu código sem definir UNIT_TESTING e quando você quer fazer a unidade testar você adicionar UNIT_TESTING #define para a primeira linha do arquivo (e escrever todo o código que faça o teste de unidade sob #if UNIT_TESTING). Que devem ser manuseados com cuidado.

Desde que eu acho que o teste de unidade é um mau exemplo para o uso de amigos, eu dar um exemplo porque eu acho que os amigos pode ser bom. Suponha que você tenha um sistema de quebra (de classe). Com o uso, o sistema quebra ficar desgastado e precisa obter renovado. Agora, você quer que apenas um mecânico licenciado iria consertá-lo. Para tornar o exemplo menos trivial eu diria que o mecânico usaria sua chave de fenda pessoal (privado) para corrigi-lo. É por isso que classe mecânico deve ser amigo de classe breakingSystem.

A amizade também pode ser simulada usando "agentes" - algumas classes internas. Considere o exemplo seguinte:

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

Poderia ser um monte mais simples quando usado para acesso apenas a membros estáticos. Benefícios para essa implementação é que todos os tipos são declarados no âmbito interno das classes de amizade e, ao contrário de interfaces, que permite que os membros estáticos para ser acessado.

eu vou responder apenas "Como" pergunta.

Há tantas respostas aqui, porém eu gostaria de propor uma espécie de "padrão de design" para alcançar esse recurso. Vou usar mecanismo de linguagem simples, que inclui:

  • Interfaces
  • classe aninhada

Por exemplo, temos 2 classes principais: Estudante e Universidade. Aluno tem GPA que só universidade permissão para acessar. Aqui está o 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($"{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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top