A palavra-chave friend C ++ permite que class A
a designe class B
como amiga. Isso permite Class B
acessar os private
/ protected
membros de class A
.
Eu nunca li nada sobre por que isso foi deixado de fora do C # (e do VB.NET). A maioria das respostas para essa pergunta anterior do StackOverflow parece estar dizendo que é uma parte útil do C ++ e há boas razões para usá-lo. Na minha experiência, eu teria que concordar.
Outra pergunta me parece realmente perguntar como fazer algo semelhante a friend
um aplicativo C #. Embora as respostas geralmente giram em torno de classes aninhadas, não parece tão elegante quanto usar a friend
palavra - chave.
O livro Design Patterns original o utiliza regularmente em todos os seus exemplos.
Então, em resumo, por que está friend
faltando no C # e qual é a maneira (ou melhor) de "prática recomendada" de simulá-lo no C #?
(A propósito, a internal
palavra-chave não é a mesma coisa, ela permite que todas as classes de toda a montagem acessem internal
membros, enquanto friend
permite que você conceda a uma certa classe acesso completo a exatamente uma outra classe)
Respostas:
Ter amigos na programação é mais ou menos considerado "sujo" e fácil de abusar. Ele quebra os relacionamentos entre classes e mina alguns atributos fundamentais de uma linguagem OO.
Dito isto, é um recurso interessante e eu já o usei várias vezes em C ++; e gostaria de usá-lo também em c #. Mas aposto que por causa da OOness "pura" do C # (em comparação com a pseudo OOness do C ++), a Microsoft decidiu que, porque Java não tinha a palavra-chave amiga, o C # também não deveria (apenas brincando;))
Em uma observação séria: interno não é tão bom quanto amigo, mas faz o trabalho. Lembre-se de que é raro você distribuir seu código para desenvolvedores de terceiros, não através de uma DLL; contanto que você e sua equipe saibam sobre as classes internas e o uso delas, você ficará bem.
EDIT Deixe-me esclarecer como a palavra-chave friend mina OOP.
Variáveis e métodos privados e protegidos são talvez uma das partes mais importantes da OOP. A idéia de que os objetos podem conter dados ou lógica que somente eles podem usar permite que você escreva sua implementação de funcionalidade independentemente do seu ambiente - e que seu ambiente não pode alterar as informações de estado que não são adequadas para lidar. Ao usar o friend, você está acoplando as implementações de duas classes - o que é muito pior do que se você apenas acoplou a interface.
fonte
friend
não permite que classes ou funções arbitrárias acessem membros privados. Ele permite que as funções ou classes específicas que foram marcadas como amigas o façam. A classe que possui o membro privado ainda controla o acesso a ele 100%. Você também pode argumentar que métodos de membros públicos. Ambos garantem que os métodos listados especificamente na classe tenham acesso a membros privados da classe.Em uma nota lateral. Usar amigo não é violar o encapsulamento, mas, pelo contrário, é impor isso. Como acessadores + mutadores, sobrecarga de operadores, herança pública, downcasting etc. , muitas vezes é mal utilizada, mas isso não significa que a palavra-chave não tenha, ou pior, um propósito ruim.
Veja a mensagem de Konrad Rudolph no outro tópico, ou se você preferir, veja a entrada relevante nas Perguntas frequentes sobre C ++.
fonte
friend
dá acesso a TODOS os seus membros privados, mesmo que você precise apenas definir um deles. Há um forte argumento a ser feito que disponibilizar campos para outra classe que não precisa deles conta como "violação do encapsulamento". Existem lugares em C ++ onde é necessário (sobrecarregando os operadores), mas há uma troca de encapsulamento que precisa ser cuidadosamente considerada. Parece que os designers de C # sentiram que a troca não valia a pena.Para informações, outra coisa relacionada, mas não exatamente a mesma no .NET
[InternalsVisibleTo]
, que permite que um assembly designe outro assembly (como um assembly de teste de unidade) que (efetivamente) tenha acesso "interno" a tipos / membros em a montagem original.fonte
Você deve conseguir o mesmo tipo de coisa que "amigo" é usado no C ++ usando interfaces em C #. Requer que você defina explicitamente quais membros estão sendo transmitidos entre as duas classes, o que é um trabalho extra, mas também pode facilitar a compreensão do código.
Se alguém tiver um exemplo de uso razoável de "amigo" que não possa ser simulado usando interfaces, compartilhe-o! Eu gostaria de entender melhor as diferenças entre C ++ e C #.
fonte
Com
friend
um designer C ++, você tem um controle preciso sobre quem os membros privados * estão expostos. Mas, ele é forçado a expor todos os membros privados.Com
internal
um designer de C #, você tem um controle preciso sobre o conjunto de membros privados que está expondo. Obviamente, ele pode expor apenas um único membro privado. Mas, ele será exposto a todas as classes na montagem.Normalmente, um designer deseja expor apenas alguns métodos particulares a outras poucas classes selecionadas. Por exemplo, em um padrão de fábrica de classe, pode ser desejável que a classe C1 seja instanciada apenas pela fábrica de classe CF1. Portanto, a classe C1 pode ter um construtor protegido e uma fábrica de classe amiga CF1.
Como você pode ver, temos duas dimensões ao longo das quais o encapsulamento pode ser violado.
friend
quebra ao longo de uma dimensão,internal
faz ao longo da outra. Qual é uma violação pior no conceito de encapsulamento? Difícil de dizer. Mas seria bom ter os doisfriend
e estarinternal
disponível. Além disso, uma boa adição a essas duas seria o terceiro tipo de palavra-chave, que seria usada membro a membro (comointernal
) e especifica a classe de destino (comofriend
).* Por uma questão de brevidade, usarei "privado" em vez de "privado e / ou protegido".
- Usuario
fonte
De fato, o C # oferece a possibilidade de obter o mesmo comportamento de maneira pura, sem palavras especiais - são interfaces privadas.
Quanto à pergunta Qual é o equivalente em C # de amigo? foi marcado como duplicado neste artigo e ninguém propõe uma realização realmente boa - mostrarei a resposta em ambas as perguntas aqui.
A ideia principal foi extrair daqui: O que é uma interface privada?
Digamos, precisamos de alguma classe que possa gerenciar instâncias de outras classes e chamar alguns métodos especiais para elas. Não queremos dar a possibilidade de chamar esses métodos para outras classes. É exatamente a mesma coisa que a palavra-chave c ++ do amigo faz no mundo c ++.
Eu acho que um bom exemplo na prática real pode ser o padrão Full State Machine, em que algum controlador atualiza o objeto de estado atual e alterna para outro objeto de estado quando necessário.
Você poderia:
Controller.cs
Program.cs
Bem, e a herança?
Precisamos usar a técnica descrita em Como as implementações explícitas dos membros da interface não podem ser declaradas virtuais e marcar IState como protegido para dar a possibilidade de derivar do Controller também.
Controller.cs
PlayerIdleState.cs
E, finalmente, exemplo de como testar a classe Controller throw herança: ControllerTest.cs
Espero cobrir todos os casos e minha resposta foi útil.
fonte
Amigo é extremamente útil ao escrever teste de unidade.
Embora isso tenha um custo de poluir um pouco a sua declaração de classe, também é um lembrete imposto pelo compilador de quais testes realmente podem se importar com o estado interno da classe.
Um idioma muito útil e limpo que encontrei é quando tenho classes de fábrica, tornando-os amigos dos itens que eles criam e que têm um construtor protegido. Mais especificamente, foi quando eu tinha uma única fábrica responsável por criar objetos de renderização correspondentes para objetos do gravador de relatórios, renderizando em um determinado ambiente. Nesse caso, você tem um único ponto de conhecimento sobre o relacionamento entre as classes de gravador de relatório (itens como blocos de figuras, faixas de layout, cabeçalhos de página etc.) e seus objetos de renderização correspondentes.
fonte
O C # está ausente na palavra-chave "friend" pelo mesmo motivo em que está faltando destruição determinística. A mudança de convenções faz com que as pessoas se sintam inteligentes, como se seus novos caminhos fossem superiores aos antigos de outras pessoas. É tudo sobre orgulho.
Dizer que "as classes de amigos são ruins" é tão míope quanto outras declarações não qualificadas, como "não use gotos" ou "Linux é melhor que o Windows".
A palavra-chave "friend" combinada com uma classe proxy é uma ótima maneira de expor apenas certas partes de uma classe a outras classes específicas. Uma classe de proxy pode atuar como uma barreira confiável contra todas as outras classes. "public" não permite tal segmentação e o uso de "protected" para obter o efeito com herança é estranho se realmente não houver um conceito "é um" relacionamento.
fonte
Você pode se aproximar de "amigo" do C ++ com a palavra-chave do C # "interna" .
fonte
internal
faz muito mais sentido e fornece uma excelente troca entre encapsulamento e utilidade. O acesso padrão do Java é ainda melhor.Na verdade, isso não é um problema com o C #. É uma limitação fundamental na IL. O c # é limitado por isso, assim como qualquer outra linguagem .Net que procure ser verificável. Essa limitação também inclui classes gerenciadas definidas em C ++ / CLI ( seção Especificações 20.5 ).
Dito isto, acho que Nelson tem uma boa explicação sobre por que isso é uma coisa ruim.
fonte
friend
não é uma coisa ruim; pelo contrário, é muito melhor queinternal
. E a explicação de Nelson é ruim e incorreta.Pare de inventar desculpas para essa limitação. amigo é ruim, mas interno é bom? eles são a mesma coisa, apenas esse amigo lhe dá um controle mais preciso sobre quem tem permissão para acessar e quem não tem.
Isso é para reforçar o paradigma de encapsulamento? então você tem que escrever métodos acessadores e agora o que? como você deve impedir que todos (exceto os métodos da classe B) chamem esses métodos? você não pode, porque também não pode controlar isso, por falta de "amigo".
Nenhuma linguagem de programação é perfeita. O C # é um dos melhores idiomas que eu já vi, mas inventar desculpas tolas para os recursos ausentes não ajuda ninguém. No C ++, sinto falta do sistema fácil de delegação / evento, reflexão (+ des / serialização automática) e foreach, mas no C # sinto falta de sobrecarga do operador (sim, continue me dizendo que você não precisava), parâmetros padrão, uma const isso não pode ser contornado, herança múltipla (sim, continue me dizendo que você não precisava disso e as interfaces eram uma substituição suficiente) e a capacidade de decidir excluir uma instância da memória (não, isso não é terrivelmente ruim a menos que você seja um funileiro)
fonte
||
/&&
mantendo-os em curto-circuito. Os parâmetros padrão foram adicionados no C # 4.Existe o InternalsVisibleToAttribute desde o .Net 3, mas suspeito que eles o tenham adicionado apenas para servir para testar assemblies após o aumento dos testes de unidade. Não vejo muitos outros motivos para usá-lo.
Funciona no nível da montagem, mas faz o trabalho onde interno não; ou seja, onde você deseja distribuir um assembly, mas deseja que outro assembly não distribuído tenha acesso privilegiado a ele.
Com toda a razão, eles exigem que a assembléia de amigos seja fortemente pressionada para evitar que alguém crie um fingido amigo ao lado de sua assembléia protegida.
fonte
Eu li muitos comentários inteligentes sobre a palavra-chave "friend" e concordo com o que é útil, mas acho que a palavra-chave "interna" é menos útil, e ambos ainda são ruins para a programação pura de OO.
O que nós temos? (dizendo sobre "amigo" e também "interno")
sim;
não está usando "amigo" torna o código melhor?
Usar friend cria alguns problemas locais, não usá-lo cria problemas para usuários da biblioteca de códigos.
a solução boa comum para linguagem de programação que eu vejo assim:
O que você acha disso? Eu acho que é a solução orientada a objetos mais comum e pura. Você pode abrir o acesso de qualquer método que escolher para qualquer classe que desejar.
fonte
Vou responder apenas a pergunta "Como".
Existem muitas respostas aqui, no entanto, eu gostaria de propor um tipo de "padrão de design" para alcançar esse recurso. Vou usar um mecanismo de linguagem simples, que inclui:
Por exemplo, temos 2 classes principais: Estudante e Universidade. O aluno possui GPA que somente a universidade tem permissão para acessar. Aqui está o código:
fonte
Eu suspeito que tenha algo a ver com o modelo de compilação C # - construindo IL o JIT que o compila em tempo de execução. ou seja: o mesmo motivo pelo qual os genéricos de C # são fundamentalmente diferentes dos genéricos de C ++.
fonte
você pode mantê-lo privado e usar a reflexão para chamar funções. A estrutura de teste pode fazer isso se você pedir para testar uma função privada
fonte
Eu costumava usar o amigo regularmente, e não acho que seja uma violação do OOP ou um sinal de qualquer falha de design. Existem vários lugares onde é o meio mais eficiente para o fim apropriado com a menor quantidade de código.
Um exemplo concreto é ao criar assemblies de interface que fornecem uma interface de comunicação para algum outro software. Geralmente, existem algumas classes pesadas que lidam com a complexidade do protocolo e das peculiaridades dos pares e fornecem um modelo de conexão / leitura / gravação / encaminhamento / desconexão relativamente simples, que envolve a transmissão de mensagens e notificações entre o aplicativo cliente e o assembly. Essas mensagens / notificações precisam ser agrupadas em classes. Os atributos geralmente precisam ser manipulados pelo software de protocolo, pois são seus criadores, mas muitas coisas precisam permanecer somente leitura para o mundo externo.
É simplesmente tolo declarar que é uma violação do OOP para a classe protocolo / "criador" ter acesso íntimo a todas as classes criadas - a classe criadora teve que mexer em todos os dados no caminho. O que eu achei mais importante é minimizar todas as linhas de código extras da BS às quais o modelo "OOP for OOP's Sake" geralmente leva. Espaguete extra apenas gera mais bugs.
As pessoas sabem que você pode aplicar a palavra-chave interna no nível de atributo, propriedade e método? Não é apenas para a declaração de classe de nível superior (embora a maioria dos exemplos pareça mostrar isso).
Se você possui uma classe C ++ que usa a palavra-chave friend e deseja emular isso em uma classe C #: 1. declare a classe C # public 2. declare todos os atributos / propriedades / métodos protegidos em C ++ e, portanto, acessíveis aos amigos como internal in C # 3. crie propriedades somente leitura para acesso público a todos os atributos e propriedades internos
Concordo que não é 100% o mesmo que amigo, e o teste de unidade é um exemplo muito valioso da necessidade de algo como amigo (como é o código de log do analisador de protocolo). No entanto, interno fornece a exposição para as classes que você deseja que seja exposto e [InternalVisibleTo ()] lida com o resto - parece que ele nasceu especificamente para teste de unidade.
Quanto ao amigo "ser melhor, porque você pode controlar explicitamente quais classes têm acesso" - o que diabos um bando de classes malignas suspeitas estão fazendo na mesma assembléia em primeiro lugar? Particione suas montagens!
fonte
A amizade pode ser simulada separando interfaces e implementações. A idéia é: " Exigir uma instância concreta, mas restringir o acesso à construção dessa instância ".
Por exemplo
Apesar de
ItsMeYourFriend()
ser pública, apenas aFriend
classe pode acessá-la, pois ninguém mais pode obter uma instância concreta daFriend
classe. Tem um construtor privado, enquanto a fábricaNew()
método retorna uma interface.Veja meu artigo Amigos e membros da interface interna sem nenhum custo com codificação para interfaces para obter detalhes.
fonte
Alguns sugeriram que as coisas podem ficar fora de controle usando o amigo. Eu concordo, mas isso não diminui sua utilidade. Não tenho certeza de que um amigo necessariamente prejudique o paradigma OO mais do que tornar todos os membros de sua classe públicos. Certamente o idioma permitirá que você publique todos os seus membros, mas é um programador disciplinado que evita esse tipo de padrão de design. Da mesma forma, um programador disciplinado reservaria o uso de friend para casos específicos em que isso faz sentido. Sinto exposições internas demais em alguns casos. Por que expor uma classe ou método a tudo na montagem?
Eu tenho uma página ASP.NET que herda minha própria página base, que por sua vez herda System.Web.UI.Page. Nesta página, tenho um código que lida com o relatório de erros do usuário final para o aplicativo em um método protegido
Agora, eu tenho um controle de usuário que está contido na página. Desejo que o controle do usuário possa chamar os métodos de relatório de erros na página.
Não é possível fazer isso se o método ReportError estiver protegido. Eu posso torná-lo interno, mas está exposto a qualquer código no assembly. Eu só quero que ele seja exposto aos elementos da interface do usuário que fazem parte da página atual (incluindo controles filho). Mais especificamente, quero que minha classe de controle base defina exatamente os mesmos métodos de relatório de erros e chame simplesmente métodos na página base.
Acredito que algo como amigo poderia ser útil e implementado na linguagem sem tornar a linguagem menos "OO", talvez como atributos, para que você possa ter classes ou métodos amigos de classes ou métodos específicos, permitindo que o desenvolvedor forneça muito acesso específico. Talvez algo como ... (pseudo código)
No caso do meu exemplo anterior, talvez tenha algo como o seguinte (pode-se argumentar semântica, mas estou apenas tentando transmitir a ideia):
A meu ver, o conceito de amigo não tem mais riscos do que tornar públicas as coisas ou criar métodos ou propriedades públicas para acessar os membros. Se alguma coisa amiga permitir outro nível de granularidade na acessibilidade dos dados e permitir que você restrinja essa acessibilidade em vez de ampliá-la com interna ou pública.
fonte
Se você estiver trabalhando com C ++ e se encontrar usando a palavra-chave friend, é uma indicação muito forte de que você tem um problema de design, por que diabos uma classe precisa acessar os membros privados de outra classe?
fonte
Bsd
Foi afirmado que amigos prejudicam pura OOness. O que eu concordo.
Também foi declarado que amigos ajudam o encapsulamento, o que também concordo.
Eu acho que a amizade deve ser adicionada à metodologia OO, mas não tanto quanto em C ++. Gostaria de ter alguns campos / métodos que minha classe de amigo possa acessar, mas NÃO gostaria que eles acessassem TODOS os meus campos / métodos. Como na vida real, eu permitia que meus amigos acessassem minha geladeira pessoal, mas não permitia que eles acessassem minha conta bancária.
Pode-se implementar isso da seguinte maneira
É claro que isso gerará um aviso do compilador e prejudicará o intellisense. Mas fará o trabalho.
Em uma nota lateral, acho que um programador confiante deve fazer a unidade de teste sem acessar os membros privados. isso está fora do escopo, mas tente ler sobre o TDD. no entanto, se você ainda deseja fazer isso (tendo c ++ como amigos), tente algo como
para que você escreva todo o seu código sem definir UNIT_TESTING e quando desejar fazer o teste de unidade, adicione #define UNIT_TESTING à primeira linha do arquivo (e escreva todo o código que faz o teste de unidade em #if UNIT_TESTING). Isso deve ser tratado com cuidado.
Como acho que o teste de unidade é um mau exemplo para o uso de amigos, eu daria um exemplo do por que acho que os amigos podem ser bons. Suponha que você tenha um sistema de interrupção (classe). Com o uso, o sistema de quebra fica desgastado e precisa ser renovado. Agora, você deseja que apenas um mecânico licenciado o conserte. Para tornar o exemplo menos trivial, eu diria que o mecânico usaria sua chave de fenda pessoal (particular) para consertá-lo. É por isso que a classe mecânica deve ser amiga da classe breakingSystem.
fonte
c.MyMethod((FriendClass) null, 5.5, 3)
de qualquer classe.A amizade também pode ser simulada usando "agentes" - algumas classes internas. Considere o seguinte exemplo:
Poderia ser muito mais simples quando usado para acessar apenas membros estáticos. Os benefícios dessa implementação são que todos os tipos são declarados no escopo interno das classes de amizade e, diferentemente das interfaces, permitem que membros estáticos sejam acessados.
fonte