Usos práticos para a palavra-chave "interna" em C #

420

Você poderia explicar qual é o uso prático da internalpalavra - chave em C #?

Sei que o internalmodificador limita o acesso ao assembly atual, mas quando e em que circunstância devo usá-lo?

Alexander Prokofyev
fonte

Respostas:

379

Classes / métodos de utilitário ou auxiliar que você gostaria de acessar de muitas outras classes dentro do mesmo assembly, mas que deseja garantir que o código em outros assemblies não possa acessar.

No MSDN (via archive.org):

Um uso comum do acesso interno é no desenvolvimento baseado em componentes, pois permite que um grupo de componentes coopere de maneira privada sem ser exposto ao restante do código do aplicativo. Por exemplo, uma estrutura para a criação de interfaces gráficas de usuário pode fornecer às classes Control e Form que cooperam usando membros com acesso interno. Como esses membros são internos, eles não são expostos ao código que está usando a estrutura.

Você também pode usar o modificador interno junto com o InternalsVisibleTo atributo de nível de montagem para criar assemblies "amigos" aos quais é concedido acesso especial às classes internas do assembly de destino.

Isso pode ser útil para a criação de montagens de teste de unidade que podem chamar membros internos da montagem para serem testados. É claro que nenhum outro assembly recebe esse nível de acesso; portanto, quando você libera seu sistema, o encapsulamento é mantido.

Cinza
fonte
8
O atributo InternalsVisibleTo é uma grande ajuda. Obrigado.
erict
33
Meu sentimento é que a partir do momento em que você precisa de algo interno, há algo errado no design em algum lugar. IMHO, um deve ser capaz de usar OO puro para resolver todos os problemas. Neste exato momento, estou olhando para o uso milionésimo de interno na fonte do .NET Framework, me impedindo de aproveitar o que eles usam. Com o uso de interno, eles criaram um monólito superficialmente modular, mas se decompõe quando você tenta isolar coisas. Além disso, a segurança não é um argumento, pois há reflexo no resgate.
Careta de desespero
26
@GrimaceofDespair: Deixe-me adicionar um comentário diagonal aqui. Pessoalmente, vejo o interno como uma versão muito útil do modificador "público" em grandes soluções de múltiplos projetos. A adição desse modificador a uma classe no meu subprojeto proíbe o "uso aleatório" dessa classe por outros subprojetos na solução e possibilita que eu force o uso adequado da minha biblioteca. Se eu precisar refatorar mais tarde, não precisarei me perguntar "espere, mas por que esse cara criou uma instância da classe Point que eu só precisava para meus próprios propósitos nesse cálculo em particular"?
KT.
3
Dito de outra maneira, se todos estivessem contando com os internos do SmtpClient e, em um momento, um bug fosse descoberto lá, o .NET teria sérios problemas para corrigi-lo, talvez até mantendo-o por muito tempo. Lembro que li um artigo divertido uma vez que descrevia o comprimento que os desenvolvedores do Windows tinham que seguir para manter a "compatibilidade com erros" de todos os programas mais antigos que faziam uso de vários recursos e bugs internos. Repetir esse erro com o .NET não é significativo.
KT.
13
Eu acho que interno é MUITO mais útil que público. A única vez que você usa public é quando você diz explicitamente "Aqui, usuário - estou fornecendo uma função útil para você usar". Se você estiver escrevendo um aplicativo em vez de uma biblioteca de usuários, tudo deve ser interno, porque você não está tentando dar às pessoas nenhuma função para chamar externamente. Se você estiver escrevendo uma biblioteca de usuários, somente as funções úteis que você espera fornecer ao usuário, manter, dar suporte e manter até o final do período devem ser públicas. Caso contrário, torne-o interno.
Darrell Plank /
85

Se Bob precisa de BigImportantClass, Bob precisa que as pessoas que possuem o projeto A se inscrevam para garantir que BigImportantClass seja gravado para atender às suas necessidades, testado para garantir que atenda às suas necessidades, seja documentado como atendendo às suas necessidades e que um processo será implementado para garantir que nunca seja alterado, de modo a não atender mais às suas necessidades.

Se uma classe é interna, ela não precisa passar por esse processo, o que economiza orçamento para o Projeto A que eles podem gastar em outras coisas.

O ponto interno não é que isso dificulte a vida de Bob. Isso permite que você controle quais promessas caras o Projeto A está fazendo sobre recursos, tempo de vida, compatibilidade e assim por diante.

Eric Lippert
fonte
5
Faz todo o sentido. Só gostaria que pudéssemos substituir os membros internos, porque somos todos adultos concordantes aqui.
Cwallenpoole
6
@EricLippert Não é bem o que eu quis dizer. Se eu herdar o código compilado que tem "interno", estou preso, certo? Não há como simplesmente dizer ao compilador "Não, que outro desenvolvedor mentiu para você. Você deveria confiar em mim", a menos que eu use reflexão.
Cwallenpoole
11
@cwallenpoole: Se você estiver usando código escrito por mentirosos que escrevem código que você não gosta, pare de usar o código e escreva seu próprio código que você mais gosta.
Eric Lippert
2
@EricLippert Isso era para ter uma língua na bochecha, eu não quis ofender. (Eu estava apenas tentando confirmar que sou SOL nesses casos).
Cwallenpoole
5
@cwallenpoole: Não estou ofendido nem um pouco. Estou oferecendo bons conselhos se você se encontrar preso nessa situação infeliz.
Eric Lippert
60

Outro motivo para usar interno é se você ofuscar seus binários. O ofuscador sabe que é seguro embaralhar o nome da classe de qualquer classe interna, enquanto o nome da classe pública não pode ser embaralhado, porque isso pode quebrar as referências existentes.

Joel Mueller
fonte
4
Pff. Observar o bytecode com um desmontador ainda deixará bem claro o que o código faz de qualquer maneira. Os ofuscadores dificilmente impedem qualquer pessoa que realmente esteja tentando entrar nos internos. Nunca ouvi falar de um hacker que parou apenas porque não recebeu nenhum nome de função útil.
Nyerguds
28
para não trancar a porta quando dorme, porque nunca ouviu falar de um ladrão parado por uma fechadura?
21717 Sergio Sergio
4
É por isso que codifico nomes de classes e variáveis ​​no código-fonte. Você pode descobrir o que é um PumpStateComparator, mas consegue adivinhar o que é um LpWj4mWE? #jobsecurity (eu não faço isso, e por favor, não realmente fazer isso, internautas pessoas.)
Slothario
32

Se você estiver escrevendo uma DLL que encapsula uma tonelada de funcionalidades complexas em uma API pública simples, “interno” é usado nos membros da classe que não devem ser expostos publicamente.

Esconder complexidade (aka encapsulamento) é o principal conceito de engenharia de software de qualidade.

Jeffrey L Whitledge
fonte
20

A palavra-chave interna é muito usada quando você está construindo um wrapper sobre código não gerenciado.

Quando você tem uma biblioteca baseada em C / C ++ que deseja DllImport, pode importar essas funções como funções estáticas de uma classe e torná-las internas, para que seu usuário tenha acesso apenas ao seu wrapper e não à API original, portanto não pode mexer com qualquer coisa. Como as funções são estáticas, você pode usá-las em qualquer lugar da montagem, para as várias classes de wrapper necessárias.

Você pode dar uma olhada no Mono.Cairo, é um invólucro da biblioteca do cairo que usa essa abordagem.

Edwin Jarvis
fonte
12

Sendo conduzido pela regra "use o modificador mais rigoroso possível", eu uso interno em todos os lugares em que preciso acessar, digamos, o método de outra classe até precisar explicitamente acessá-lo em outro assembly.

Como a interface de montagem geralmente é mais estreita que a soma das interfaces de classes, existem muitos lugares em que eu a uso.

Ilya Komakhin
fonte
3
Se você "usa o modificador mais rigoroso possível", por que não privado?
R. Martinho Fernandes
6
Como private é muito rigoroso quando propriedades / métodos de classe precisam ser acessados ​​fora da classe e casos em que devem ser acessados ​​de outro asse, bly não é tão frequente.
Ilya Komakhin
11

Acho interno muito usado em demasia. você realmente não deve expor certas funcionalidades apenas a determinadas classes que não exporia a outros consumidores.

Na minha opinião, isso quebra a interface, quebra a abstração. Isso não quer dizer que nunca deva ser usado, mas uma solução melhor é refatorar para uma classe diferente ou ser usado de uma maneira diferente, se possível. No entanto, isso nem sempre é possível.

O motivo pelo qual isso pode causar problemas é que outro desenvolvedor pode ser encarregado de criar outra classe no mesmo assembly que o seu. Ter internos diminui a clareza da abstração e pode causar problemas se for mal utilizado. Seria o mesmo problema como se você o tornasse público. A outra classe que está sendo construída pelo outro desenvolvedor ainda é um consumidor, assim como qualquer classe externa. Abstração e encapsulamento de classe não são apenas para proteção de / para classes externas, mas para toda e qualquer classe.

Outro problema é que muitos desenvolvedores pensam que podem precisar usá-lo em outro local da montagem e marcam como interno de qualquer maneira, mesmo que não precisem no momento. Outro desenvolvedor, então, pode pensar que está lá para ser usado. Normalmente, você deseja marcar como privado até ter uma necessidade definitiva.

Mas parte disso pode ser subjetiva, e não estou dizendo que nunca deva ser usado. Basta usar quando necessário.

mattlant
fonte
Costumo criar objetos de domínio para os quais eles têm membros que devem ser públicos para outros objetos de domínio dentro do mesmo assembly. No entanto, esses mesmos membros NÃO devem estar disponíveis para o código do cliente que está fora do assembly. Para situações como essa, 'interno' é muito apropriado.
Rock Anthony Johnson
8

Vi um interessante outro dia, talvez semana, em um blog que não me lembro. Basicamente, não posso receber crédito por isso, mas achei que poderia ter alguma aplicação útil.

Digamos que você queira que uma classe abstrata seja vista por outra montagem, mas não que alguém possa herdar dela. O Sealed não funcionará porque é abstrato por um motivo, outras classes nesse assembly são herdadas dele. Privado não funcionará porque você pode declarar uma classe Pai em algum lugar da outra montagem.

namespace Base.Assembly
{
  public abstract class Parent
  {
    resumo interno vazio SomeMethod ();
  }

  // Isso funciona muito bem, pois está na mesma montagem.
  public class ChildWithin: Parent
  {
    substituição interna void SomeMethod ()
    {
    }
  }
}

namespace Another.Assembly
{
  // Kaboom, porque você não pode substituir um método interno
  public class ChildOutside: Parent
  {
  }

  Teste de classe pública 
  { 

    //Bem
    private Parent _parent;

    Teste público ()
    {
      //Continua tudo bem
      _parent = new ChildWithin ();
    }
  }
}

Como você pode ver, ele efetivamente permite que alguém use a classe Parent sem poder herdar.

Ferramenta Programmin
fonte
7

Este exemplo contém dois arquivos: Assembly1.cs e Assembly2.cs. O primeiro arquivo contém uma classe base interna, BaseClass. No segundo arquivo, uma tentativa de instanciar BaseClass produzirá um erro.

// Assembly1.cs
// compile with: /target:library
internal class BaseClass 
{
   public static int intM = 0;
}

// Assembly1_a.cs
// compile with: /reference:Assembly1.dll
class TestAccess 
{
   static void Main()
   {  
      BaseClass myBase = new BaseClass();   // CS0122
   }
}

Neste exemplo, use os mesmos arquivos que você usou no exemplo 1 e altere o nível de acessibilidade do BaseClass para público . Altere também o nível de acessibilidade do membro IntM para interno . Nesse caso, você pode instanciar a classe, mas não pode acessar o membro interno.

// Assembly2.cs
// compile with: /target:library
public class BaseClass 
{
   internal static int intM = 0;
}

// Assembly2_a.cs
// compile with: /reference:Assembly1.dll
public class TestAccess 
{
   static void Main() 
   {      
      BaseClass myBase = new BaseClass();   // Ok.
      BaseClass.intM = 444;    // CS0117
   }
}

fonte : http://msdn.microsoft.com/en-us/library/7c5ka91b(VS.80).aspx

Murad Mohd Zain
fonte
6

Quando você tem métodos, classes, etc., que precisam estar acessíveis no escopo da montagem atual e nunca fora dela.

Por exemplo, um DAL pode ter um ORM, mas os objetos não devem ser expostos à camada de negócios; toda a interação deve ser feita através de métodos estáticos e passando os parâmetros necessários.

Aaron Powell
fonte
6

Um uso muito interessante de interno - com o membro interno, é claro, limitado apenas à montagem em que é declarado - está obtendo a funcionalidade "amiga" em algum grau. Um membro amigo é algo que é visível apenas para certas outras assembléias fora da assembléia na qual foi declarado. O C # não possui suporte interno para amigo, no entanto, o CLR sim.

Você pode usar InternalsVisibleToAttribute para declarar um assembly amigo, e todas as referências de dentro do assembly amigo tratarão os membros internos do assembly declarante como públicos no escopo do assembly amigo. Um problema com isso é que todos os membros internos são visíveis; você não pode escolher e escolher.

Um bom uso para InternalsVisibleTo é expor vários membros internos a um conjunto de teste de unidade, eliminando assim as necessidades de soluções complexas de reflexão para testar esses membros. Todos os membros internos visíveis não são um grande problema, no entanto, adotar essa abordagem estraga bastante as interfaces de classe e pode arruinar o encapsulamento dentro do conjunto declarante.

cfeduke
fonte
5

Como regra geral, existem dois tipos de membros:

  • superfície pública : visível a partir de uma montagem externa (pública, protegida e protegida interna): o chamador não é confiável; portanto, é necessária a validação de parâmetros, a documentação do método etc.
  • superfície privada : não visível de um assembly externo (classes privada e interna ou interna): o chamador geralmente é confiável, portanto, a validação de parâmetros, a documentação de métodos etc. podem ser omitidos.
Michael Damatov
fonte
4

Redução de ruído, quanto menos tipos você expõe, mais simples é sua biblioteca. A prova de adulteração / segurança é outra (embora o Reflection possa vencer contra ele).

Quibblesome
fonte
4

Classes internas permitem limitar a API do seu assembly. Isso tem benefícios, como tornar sua API mais simples de entender.

Além disso, se houver um bug no seu assembly, há menos chances de a correção introduzir uma alteração de quebra. Sem as classes internas, você teria que assumir que mudar os membros públicos de qualquer classe seria uma mudança de quebra. Com classes internas, você pode assumir que a modificação de seus membros públicos apenas quebra a API interna do assembly (e quaisquer assemblies mencionados no atributo InternalsVisibleTo).

Eu gosto de ter encapsulamento no nível da classe e no nível da montagem. Há quem discorde disso, mas é bom saber que a funcionalidade está disponível.

xofz
fonte
2

Eu tenho um projeto que usa LINQ-to-SQL para o back-end de dados. Eu tenho dois namespaces principais: Biz e Data. O modelo de dados LINQ reside em Data e está marcado como "interno"; o espaço para nome Biz possui classes públicas que envolvem as classes de dados LINQ.

Então tem Data.Client, e Biz.Client; o último expõe todas as propriedades relevantes do objeto de dados, por exemplo:

private Data.Client _client;
public int Id { get { return _client.Id; } set { _client.Id = value; } }

Os objetos Biz têm um construtor privado (para forçar o uso de métodos de fábrica) e um construtor interno que se parece com isso:

internal Client(Data.Client client) {
    this._client = client;
}

Isso pode ser usado por qualquer uma das classes de negócios na biblioteca, mas o front-end (UI) não tem como acessar diretamente o modelo de dados, garantindo que a camada de negócios sempre atue como intermediária.

Esta é a primeira vez que eu realmente usei internalmuito, e está se mostrando bastante útil.

Keith Williams
fonte
2

Há casos em que faz sentido formar membros de classes internal. Um exemplo poderia ser se você deseja controlar como as classes são instanciadas; digamos que você forneça algum tipo de fábrica para criar instâncias da classe. Você pode criar o construtor internal, para que a fábrica (que reside no mesmo assembly) possa criar instâncias da classe, mas o código fora desse assembly não possa.

No entanto, não vejo sentido em fazer aulas ou membros internalsem razões específicas, tão pouco quanto faz sentido public, ou privatesem razões específicas.

Fredrik Mörk
fonte
1

a única coisa em que já usei a palavra-chave interna é o código de verificação de licença no meu produto ;-)

Steven A. Lowe
fonte
1

Um uso da palavra-chave interna é limitar o acesso a implementações concretas do usuário de sua montagem.

Se você possui uma fábrica ou algum outro local central para a construção de objetos, o usuário da sua montagem precisa lidar apenas com a interface pública ou a classe base abstrata.

Além disso, construtores internos permitem controlar onde e quando uma classe pública é instanciada.

Andrew Kennan
fonte
1

Que tal este: normalmente é recomendável não expor um objeto de lista a usuários externos de um assembly, mas expor um IEnumerable. Mas é muito mais fácil usar um objeto List dentro do assembly, porque você obtém a sintaxe da matriz e todos os outros métodos de List. Portanto, normalmente tenho uma propriedade interna que expõe uma Lista a ser usada dentro da montagem.

Comentários são bem-vindos sobre essa abordagem.

Samik R
fonte
@ Samik - você poderia, por favor, especificar: "normalmente é recomendável que você não exponha um objeto de lista a usuários externos de uma montagem, exponha um IEnumerable". Por que o ienumerable é preferido?
precisa saber é o seguinte
1
@ Howiecamp: Principalmente porque o IEnumerable concede um acesso somente leitura à lista, onde, como se você a expusesse diretamente, ela pode ser modificada pelos clientes (como excluir elementos etc.), que podem não ser intencionais.
precisa saber é o seguinte
Além disso, a exposição de uma lista ou IList cria um contrato que você sempre permitirá (por exemplo) acesso aleatório rápido aos dados. Se algum dia você decidir mudar seu método para usar outra coleção, ou nenhuma coleção (retorno de rendimento), você precisará criar uma Lista apenas para retornar um valor ou quebrar o contrato alterando o tipo de retorno do método. O uso de um tipo de retorno menos específico oferece flexibilidade.
1

Lembre-se de que qualquer classe definida como publicserá exibida automaticamente no intellisense quando alguém examinar o namespace do seu projeto. Da perspectiva da API, é importante mostrar apenas aos usuários do seu projeto as classes que eles podem usar. Use a internalpalavra-chave para ocultar coisas que eles não deveriam ver.

Se o seu Big_Important_Classpara o Projeto A se destina a ser usado fora do seu projeto, você não deve marcá-lo internal.

No entanto, em muitos projetos, muitas vezes você tem classes que realmente são destinadas apenas para uso dentro de um projeto. Por exemplo, você pode ter uma classe que mantém os argumentos para uma chamada de encadeamento parametrizada. Nesses casos, você deve marcá-los como internalse por nenhum outro motivo, a fim de se proteger de uma alteração não intencional da API no futuro.

Matt Davis
fonte
Então, por que não usar privado?
Prisioneiro ZERO
1
A privatepalavra-chave pode ser usada apenas em classes ou estruturas definidas em outra classe ou estrutura. Uma classe definida em um espaço para nome só pode ser declarada publicou internal.
Matt Davis
1

A idéia é que, quando você estiver projetando uma biblioteca, apenas as classes destinadas a serem usadas de fora (pelos clientes da sua biblioteca) sejam públicas. Dessa forma, você pode ocultar classes que

  1. É provável que sejam alteradas em versões futuras (se fossem públicas, você quebraria o código do cliente)
  2. São inúteis para o cliente e podem causar confusão
  3. Não são seguros (o uso inadequado pode prejudicar bastante sua biblioteca)

etc.

Se você estiver desenvolvendo soluções internas, o uso de elementos internos não é tão importante, eu acho, porque geralmente os clientes terão contato constante com você e / ou acesso ao código. Eles são bastante críticos para os desenvolvedores de bibliotecas.

Grzenio
fonte
-7

Quando você tem classes ou métodos que não se encaixam perfeitamente no Paradigma Orientado a Objetos, que fazem coisas perigosas, que precisam ser chamadas de outras classes e métodos sob seu controle e que você não deseja permitir que outras pessoas usem .

public class DangerousClass {
    public void SafeMethod() { }
    internal void UpdateGlobalStateInSomeBizarreWay() { }
}
yfeldblum
fonte
O que você está tentando dizer? Não está muito claro. Pelo menos não para mim.
Pqsk 6/09/12
De acordo com @pqsk
Anynomous Khan 03/02