Na postagem do blog de Kathleen Dollard de 2008 , ela apresenta um motivo interessante para usar classes aninhadas em .net. No entanto, ela também menciona que FxCop não gosta de classes aninhadas. Estou assumindo que as pessoas que escrevem as regras do FxCop não são estúpidas, então deve haver um raciocínio por trás dessa posição, mas não fui capaz de descobrir.
93
Respostas:
Use uma classe aninhada quando a classe que você está aninhando só for útil para a classe envolvente. Por exemplo, classes aninhadas permitem que você escreva algo como (simplificado):
Você pode fazer uma definição completa de sua classe em um só lugar, você não tem que pular nenhum obstáculo PIMPL para definir como sua classe funciona, e o mundo externo não precisa ver nada de sua implementação.
Se a classe TreeNode fosse externa, você teria que criar todos os campos
public
ou váriosget/set
métodos para usá-la. O mundo exterior teria outra classe poluindo seu intelecto.fonte
Do Tutorial de Java da Sun:
Por que usar classes aninhadas? Existem vários motivos convincentes para o uso de classes aninhadas, entre eles:
Agrupamento lógico de classes - se uma classe for útil apenas para uma outra classe, então é lógico incorporá-la nessa classe e manter as duas juntas. Aninhar essas "classes auxiliares" torna seu pacote mais simplificado.
Encapsulamento aumentado - Considere duas classes de nível superior, A e B, onde B precisa de acesso aos membros de A que, de outra forma, seriam declarados privados. Ao ocultar a classe B dentro da classe A, os membros de A podem ser declarados privados e B pode acessá-los. Além disso, o próprio B pode ser escondido do mundo exterior.<- Isso não se aplica à implementação do C # de classes aninhadas, isso se aplica apenas ao Java.Código mais legível e sustentável - aninhar pequenas classes em classes de nível superior coloca o código mais perto de onde ele é usado.
fonte
Padrão singleton totalmente preguiçoso e thread-safe
fonte: http://www.yoda.arachsys.com/csharp/singleton.html
fonte
Depende do uso. Eu raramente usaria uma classe aninhada Public, mas usaria classes aninhadas Private o tempo todo. Uma classe aninhada privada pode ser usada para um subobjeto que deve ser usado apenas dentro do pai. Um exemplo disso seria se uma classe HashTable contivesse um objeto Entry privado para armazenar dados apenas internamente.
Se a classe se destina a ser usada pelo chamador (externamente), geralmente gosto de torná-la uma classe autônoma separada.
fonte
Além dos outros motivos listados acima, há mais um motivo pelo qual posso pensar não apenas em usar classes aninhadas, mas, de fato, classes públicas aninhadas. Para aqueles que trabalham com várias classes genéricas que compartilham os mesmos parâmetros de tipo genérico, a capacidade de declarar um namespace genérico seria extremamente útil. Infelizmente, .Net (ou pelo menos C #) não apóia a ideia de namespaces genéricos. Portanto, para atingir o mesmo objetivo, podemos usar classes genéricas para cumprir o mesmo objetivo. Veja as seguintes classes de exemplo relacionadas a uma entidade lógica:
Podemos simplificar as assinaturas dessas classes usando um namespace genérico (implementado por meio de classes aninhadas):
Então, por meio do uso de classes parciais, conforme sugerido por Erik van Brakel em um comentário anterior, você pode separar as classes em arquivos aninhados separados. Eu recomendo usar uma extensão do Visual Studio como NestIn para oferecer suporte ao aninhamento de arquivos de classe parcial. Isso permite que os arquivos de classe "namespace" também sejam usados para organizar os arquivos de classe aninhados em uma pasta.
Por exemplo:
Entity.cs
Entity.BaseDataObject.cs
Entity.BaseDataObjectList.cs
Entity.IBaseBusiness.cs
Entity.IBaseDataAccess.cs
Os arquivos no explorador de soluções do Visual Studio seriam organizados da seguinte forma:
E você implementaria o namespace genérico como o seguinte:
User.cs
User.DataObject.cs
User.DataObjectList.cs
User.IBusiness.cs
User.IDataAccess.cs
E os arquivos seriam organizados no explorador de soluções da seguinte maneira:
O exemplo acima é um exemplo simples de uso de uma classe externa como um namespace genérico. Eu construí "namespaces genéricos" contendo 9 ou mais parâmetros de tipo no passado. Ter que manter esses parâmetros de tipo sincronizados entre os nove tipos que todos precisavam saber os parâmetros de tipo era tedioso, especialmente ao adicionar um novo parâmetro. O uso de namespaces genéricos torna esse código muito mais gerenciável e legível.
fonte
Se entendi direito o artigo de Katheleen, ela propõe o uso de classe aninhada para poder escrever SomeEntity.Collection em vez de EntityCollection <SomeEntity>. Em minha opinião, é uma forma polêmica de economizar um pouco de digitação. Tenho certeza de que nas coleções de aplicativos do mundo real haverá alguma diferença nas implementações, portanto, você precisará criar classes separadas. Acho que usar o nome da classe para limitar o escopo de outra classe não é uma boa ideia. Ele polui o intellisense e fortalece as dependências entre as classes. O uso de namespaces é uma forma padrão de controlar o escopo das classes. No entanto, acho que o uso de classes aninhadas como no comentário @hazzen é aceitável, a menos que você tenha toneladas de classes aninhadas, o que é um sinal de projeto ruim.
fonte
Costumo usar classes aninhadas para ocultar detalhes de implementação. Um exemplo da resposta de Eric Lippert aqui:
Esse padrão fica ainda melhor com o uso de genéricos. Veja esta questão para dois exemplos legais. Então eu acabo escrevendo
ao invés de
Também posso ter uma lista genérica de,
Equality<Person>
mas nãoEqualityComparer<Person, int>
enquanto que
não é possível. Esse é o benefício da classe aninhada herdada da classe pai.
Outro caso (da mesma natureza - ocultar a implementação) é quando você deseja tornar os membros de uma classe (campos, propriedades, etc.) acessíveis apenas para uma única classe:
fonte
Outro uso ainda não mencionado para classes aninhadas é a segregação de tipos genéricos. Por exemplo, suponha que se queira ter algumas famílias genéricas de classes estáticas que podem usar métodos com vários números de parâmetros, junto com valores para alguns desses parâmetros, e gerar delegados com menos parâmetros. Por exemplo, deseja-se ter um método estático que pode pegar um
Action<string, int, double>
e gerar aString<string, int>
que chamará a ação fornecida passando 3.5 como odouble
; pode-se também desejar ter um método estático que pode tomar um anAction<string, int, double>
e produzir umAction<string>
, passando7
porint
e5.3
comodouble
. Usando classes aninhadas genéricas, pode-se fazer com que as invocações de método sejam algo como:ou, porque os últimos tipos em cada expressão podem ser inferidos, embora os primeiros não possam:
Usar os tipos genéricos aninhados torna possível dizer quais delegados são aplicáveis a quais partes da descrição geral do tipo.
fonte
As classes aninhadas podem ser usadas para as seguintes necessidades:
fonte
Como nawfal mencionou a implementação do padrão Abstract Factory, esse código pode ser estendido para atingir o padrão Class Clusters que é baseado no padrão Abstract Factory.
fonte
Gosto de aninhar exceções que são exclusivas de uma única classe, ou seja, aqueles que nunca são lançados de qualquer outro lugar.
Por exemplo:
Isso ajuda a manter seus arquivos de projeto organizados e não cheios de uma centena de pequenas classes de exceção atarracadas.
fonte
Lembre-se de que você precisará testar a classe aninhada. Se for privado, você não poderá testá-lo isoladamente.
Você pode torná-lo interno, no entanto, em conjunto com o
InternalsVisibleTo
atributo . No entanto, isso seria o mesmo que tornar um campo privado interno apenas para fins de teste, o que considero uma autodocumentação ruim.Portanto, você pode querer implementar apenas classes aninhadas privadas envolvendo baixa complexidade.
fonte
sim para este caso:
fonte
Com base no meu entendimento desse conceito, poderíamos usar esse recurso quando as classes estiverem relacionadas umas às outras conceitualmente. Quero dizer, alguns deles são um item completo em nosso negócio, como entidades que existem no mundo DDD que ajudam um objeto raiz agregado a completar sua lógica de negócios.
Para esclarecer, vou mostrar isso por meio de um exemplo:
Imagine que temos duas classes como Order e OrderItem. Na classe de pedido, vamos gerenciar todos os orderItems e em OrderItem estamos mantendo dados sobre um único pedido para esclarecimento, você pode ver as classes abaixo:
fonte