Por que usar um método público em uma classe interna?

250

Há muito código em um de nossos projetos que se parece com isso:

internal static class Extensions
{
    public static string AddFoo(this string s)
    {
        if (s == null)
        {
            return "Foo";
        }

        return $({s}Foo);
    }
}

Existe algum motivo explícito para fazer isso além de "é mais fácil tornar o tipo público posteriormente?"

Eu suspeito que isso só importa em casos extremos muito estranhos (reflexo no Silverlight) ou não.

bopapa_1979
fonte
1
Na minha experiência, essa é a prática normal.
Phoog
2
@phoog, OK. mas por que essa é a prática normal? Talvez seja mais fácil do que mudar vários métodos para internos? Solução rápida -> marque o tipo interno?
Bopapa_1979
isso é o que eu sempre assumi. Eu não tenho uma análise bem pensada, no entanto, é por isso que não postei uma resposta.
Phoog
2
A resposta de Eric Lippert resume muito bem meu pensamento e também traz à tona algo que estava oculto no meu cérebro, tentando encontrar uma saída: implementações implícitas de membros da interface devem ser públicas.
Phoog
Esse método não é igual a return s + "Foo";? O +operador não se importa com cadeias nulas ou vazias.
Mikkel R. Lund

Respostas:

418

ATUALIZAÇÃO: Esta questão foi o assunto do meu blog em setembro de 2014 . Obrigado pela ótima pergunta!

Há um debate considerável sobre essa questão, mesmo dentro da própria equipe do compilador.

Primeiro, é aconselhável entender as regras. Um membro público de uma classe ou estrutura é um membro acessível a qualquer coisa que possa acessar o tipo de contenção . Portanto, um membro público de uma classe interna é efetivamente interno.

Então agora, dada uma classe interna, os membros que você deseja acessar na assembléia devem ser marcados como públicos ou internos?

Minha opinião é: marque esses membros como públicos.

Eu uso "público" para significar "este membro não é um detalhe de implementação". Um membro protegido é um detalhe de implementação; há algo sobre isso que será necessário para fazer uma classe derivada funcionar. Um membro interno é um detalhe de implementação; outra coisa interna a esta montagem precisa do membro para funcionar corretamente. Um membro público diz "esse membro representa a funcionalidade documentada chave fornecida por esse objeto".

Basicamente, minha atitude é: suponha que decidi transformar essa classe interna em pública. Para fazer isso, quero mudar exatamente uma coisa : a acessibilidade da classe. Se transformar uma classe interna em uma classe pública significa que eu também preciso transformar um membro interno em um membro público, esse membro fazia parte da área pública da classe e deveria ter sido público em primeiro lugar.

Outras pessoas discordam. Há um contingente que diz que eles querem poder dar uma olhada na declaração de um membro e saber imediatamente se ela será chamada apenas a partir do código interno.

Infelizmente, isso nem sempre funciona bem; por exemplo, uma classe interna que implementa uma interface interna ainda precisa ter os membros de implementação marcados como públicos, porque fazem parte da superfície pública da classe .

Eric Lippert
fonte
11
Gosto dessa resposta ... ela se encaixa bem na minha atitude em relação à escrita de um código que é auto-documentado na maior extensão possível e o escopo é uma ótima maneira de transmitir a intenção, especialmente se os outros membros da equipe entenderem sua convenção.
bopapa_1979
10
+1 por dizer o que eu queria dizer, mas formulá-lo muito melhor do que eu era capaz (também por trazer o ângulo da interface, embora note que isso é verdade apenas para implementações implícitas de membros).
Phoog
2
A parte Interface é importante - é impossível implementar um método de interface usando interno, seja declaração de interface pública ou explícita. Portanto, a palavra público está sobrecarregada com dois significados.
Michael Stum
8
De uma perspectiva idealógica, seu argumento a favor publicé muito convincente. No entanto, acho que "nem sempre funciona bem ..." para ser especialmente poderoso. A perda de consistência desvaloriza todos os benefícios "de relance". Usar internaldessa maneira significa criar deliberadamente intuições que às vezes estão erradas. Ter uma intuição quase certa é IMO uma coisa horrível para se programar.
18712 Brian Brian
3
Citação importante da sua postagem no blog: "Meu conselho é discutir o problema entre sua equipe, tomar uma decisão e segui-lo".
bopapa_1979
17

Se a classe for internal, não importa do ponto de vista da acessibilidade se você marca um método internalou public. No entanto, ainda é bom usar o tipo que você usaria se a classe fosse public.

Enquanto alguns disseram que isso facilita as transições de internalpara public. Também serve como parte da descrição do método. Internalos métodos normalmente são considerados inseguros para acesso irrestrito, enquanto os publicmétodos são considerados (principalmente) jogos gratuitos.

Ao usar internalou publiccomo faria em uma publicclasse, você garante que está comunicando qual estilo de acesso é esperado, além de facilitar o trabalho necessário para tornar a classe publicno futuro.

Guvante
fonte
Costumo limitar tudo ao máximo possível, criando uma boa API e permitindo que o consumidor pretendido faça o que pretendo. Também estou com você para marcar o membro como faria se a turma fosse pública. Mais especificamente, eu marcaria qualquer membro como público se eu considerasse sempre seguro. Caso contrário, outra pessoa que marcar a classe como pública mais tarde (acontece) pode expor membros não seguros.
22412 bopapa_1979
@ Eric: Se você deseja renunciar a determinar a segurança de um método até estar pronto, tudo bem, mas eu estava me referindo apenas a métodos considerados seguros, caso em que não há exposição.
Guvante
10

Costumo marcar meus métodos em classes internas como públicos, em vez de internos, pois a) realmente não importa eb) eu uso interno para indicar que o método é interno de propósito (há alguma razão pela qual não quero expor isso Portanto, se eu tiver um método interno, realmente preciso entender o motivo pelo qual ele é interno antes de alterá-lo para público, enquanto que, se estiver lidando com um método público em uma classe interna, realmente tenho que pensar por que o método A classe é interna e não o motivo pelo qual cada método é interno.

Ɖ diamante ǤeezeƦ
fonte
Obrigado pela resposta. I tendem a insistir teimosamente que as questões de "tudo" quando codificação, embora :)
bopapa_1979
1
Você está certo, Eric, foi um pouco de um comentário de jogar fora. Espero que o resto da minha resposta tenha sido útil. Acho que a resposta de Eric Lippert é o que eu estava tentando expressar, mas ele explicou muito melhor.
Ɖiamond ǤeezeƦ
8

Eu suspeito que "é mais fácil tornar o tipo público mais tarde?" é isso.

As regras de escopo significam que o método só será visível como internal- portanto, não importa se os métodos estão marcados publicouinternal .

Uma possibilidade que vem à mente é que a classe era pública e posteriormente foi alterada para internale o desenvolvedor não se deu ao trabalho de alterar todos os modificadores de acessibilidade do método.

Oded
fonte
1
+1 por pregar o óbvio cenário "classe pública tornou-se interno".
bopapa_1979
8

Em alguns casos, também pode ser que o tipo interno implemente uma interface pública, o que significaria que qualquer método definido nessa interface ainda precisaria ser declarado como público.

Trevor Pilley
fonte
2

É o mesmo, o método público será realmente marcado como interno, pois está dentro de uma classe interna, mas possui uma vantagem (como você convidou); se você quiser marcar a classe como pública, precisará alterar menos código.

Mario Corchero
fonte
2
já existe uma pergunta relacionada: stackoverflow.com/questions/711361/…
Mario Corchero 15/02/2012
0

internaldiz que o membro só pode ser acessado de dentro do mesmo assembly. Outras classes nessa montagem podem acessar o internal publicmembro, mas não poderiam acessar um privateou protectedmembro, internalou não.

Ryan P
fonte
Mas se os métodos fossem marcados em internalvez de public, sua visibilidade não mudaria. Acredito que é sobre isso que o OP está perguntando.
Oded
1
@ zapthedingbat - a postagem está correta, mas está realmente respondendo à pergunta?
Oded
1
Certo, a questão é por que marcá-los dessa maneira. Eu entendo o básico do escopo. Obrigado pela tentativa, no entanto!
22412 bopapa_1979
0

Na verdade, eu lutei com isso hoje. Até agora, eu teria dito que todos os métodos deveriam ser marcados com internalse a classe fosse internale consideraria qualquer outra coisa simplesmente como mau código ou preguiça, especialmente no desenvolvimento da empresa; no entanto, eu tive que subclassificar uma publicclasse e substituir um de seus métodos:

internal class SslStreamEx : System.Net.Security.SslStream
{
    public override void Close()
    {
        try
        {
            // Send close_notify manually
        }
        finally
        {
            base.Close();
        }
    }
}

O método DEVE ser publice me fez pensar que não há realmente nenhum ponto lógico para definir métodos, a internalmenos que eles realmente devam ser, como disse Eric Lippert.

Até agora, eu nunca parei para pensar sobre isso, apenas aceitei, mas depois de ler o post de Eric, isso realmente me fez pensar e depois de muita deliberação, faz muito sentido.

Tempestade
fonte
0

Existe uma diferença. Em nosso projeto, tornamos muitas classes internas, mas fazemos testes de unidade em outra montagem e, em nossas informações de montagem, usamos InternalsVisibleTo para permitir que o assembly UnitTest chame as classes internas. Eu notei que se a classe interna tiver um construtor interno, não conseguiremos criar uma instância usando Activator.CreateInstance no assembly de teste de unidade por algum motivo. Mas se mudarmos o construtor para público, mas a classe ainda for interna, funcionará bem. Mas acho que esse é um caso muito raro (como Eric disse no post original: Reflexão).

Farrah Jiang
fonte
0

Eu acho que tenho uma opinião adicional sobre isso. No começo, eu estava pensando em como faz sentido declarar algo para o público em uma classe interna. Então acabei aqui, lendo que poderia ser bom se você decidir mudar a classe para público mais tarde. Verdade. Então, um padrão se formou em minha mente: se não alterar o comportamento atual, seja permissivo e permita coisas que não fazem sentido (e não prejudicam) no estado atual do código, mas mais tarde, se você altere a declaração da classe.

Como isso:

public sealed class MyCurrentlySealedClass
{
    protected void MyCurretlyPrivateMethod()
    {
    }
}

De acordo com o "padrão" que mencionei acima, isso deve estar perfeitamente correto. Segue a mesma ideia. Ele se comporta como um privatemétodo, pois você não pode herdar a classe. Mas se você excluir a sealedrestrição, ela ainda é válida: as classes herdadas podem ver esse método, que é absolutamente o que eu queria alcançar. Mas você recebe um aviso:, CS0628ou CA1047. Ambos são sobre não declarar protectedmembros em uma sealedclasse. Além disso, encontrei total acordo sobre o que não faz sentido: aviso de 'membro protegido em classe selada' (classe única)

Então, após esse aviso e a discussão vinculados, decidi tornar tudo interno ou menos, em uma classe interna, porque isso conforma mais esse tipo de pensamento, e não misturamos "padrões" diferentes.

Olá x
fonte
Eu prefiro drasticamente fazê-lo da maneira (ou pelo menos pelas razões), disse Eric Lippert. Definitivamente, vale a pena ler seu artigo.
Bopapa_1979