Regra de uma classe por arquivo no .NET? [fechadas]


185

Eu sigo essa regra, mas alguns de meus colegas discordam dela e argumentam que, se uma classe é menor, ela pode ser deixada no mesmo arquivo com outras classes.

Outro argumento que ouço o tempo todo é: "Mesmo a Microsoft não faz isso, então por que deveríamos?"

Qual é o consenso geral sobre isso? Existem casos em que isso deve ser evitado?


1
possível duplicação de classes C # em arquivos separados?

Respostas:


176

Uma classe por arquivo também oferece uma idéia melhor do que cada check-in está mudando sem examinar as diferenças do arquivo.


4
Mais classes no mesmo arquivo aprimoram a diferença entre classes relacionadas em apenas uma operação.
Luca

3
Os visualizadores de diferenças adequados permitem visualizar todas as diferenças de um commit.
Dykam 12/03/10

44
Não tenho muita certeza de como essa resposta específica é a resposta para as perguntas da postagem original. Claro que fornece um motivo para manter uma classe em um arquivo (embora Luca e Dykam façam bons comentários), mas não reflete consenso geral ou fornece casos em que deve ser evitado.
Robert Davis

4
As melhores práticas não existem, se você acredita que as melhores práticas entendem que elas se aplicam apenas a um determinado contexto. Como outras respostas apontam, há momentos em que essa regra realmente cria um código menos sustentável. À medida que as ferramentas melhoram, essa regra se torna menos relevante, pois é muito mais fácil encontrar classes e tipos em um projeto.
abombss

263

Eu odeio quando as pessoas pensam no absoluto e dizem que você nunca deve fazer isso ou aquilo com algo subjetivo e exigente como esse, como se todos precisássemos nos conformar com a idéia estúpida de alguém de certo e errado. Conclusão: ter mais de uma classe por arquivo é totalmente bom se fizer sentido. Por faz sentido, quero dizer coisas como:

  1. Torna o código mais fácil de digerir e manter
  2. Torna a solução menos irritante (percorrer inúmeros arquivos desnecessários) e menos lenta
  3. A equipe de desenvolvimento está de acordo com isso como uma prática de codificação local

Um bom exemplo do motivo pelo qual desejo várias classes por arquivo:

Digamos que eu tenha algumas dezenas de classes de exceção personalizadas, cada uma com 4 linhas, eu poderia ter um arquivo separado para cada uma ou agrupar as exceções e ter um arquivo por grupo. Para mim, o que parece a abordagem mais racional / pragmática é agrupá-los e ter apenas alguns arquivos, porque é mais eficiente em termos de tempo / codificação (não preciso clicar com o botão direito do mouse -> Adicionar classe, renomear 50 vezes) , mantém a solução menos confusa e com melhor desempenho.


92
+1.000. Compreender a lógica das melhores práticas e pensar duas vezes antes de violá-las é ótimo. A adesão servil é má.
dsimcha

19
Algumas dezenas de classes de exceção personalizadas ? Essa não é a verdadeira fonte do problema? Não pretendo apenas ser exigente aqui: acho que na maioria das vezes as pessoas querem combinar classes em um único arquivo, é porque estão criando desnecessariamente muitos tipos. (Dito isto, talvez haja um caso real em que algumas dezenas de classes de exceção personalizadas façam sentido). Numerosas classes pequenas e não operacionais são normalmente um sinal de um problema maior.
Jeff Sternal

16
Eu concordo com você na maior parte. Mas as exceções são um caso especial. Como um sistema adequadamente arquitetado precisa ter um tratamento de exceção adequado para lidar com todos os casos que geram erros legítimos, a maioria dos artigos de práticas recomendadas que li enfatizam a necessidade de criar exceções específicas (e algumas vezes você precisa delas para abranja todas as regras de negócios de um projeto grande) para que você não acabe capturando system.exception que não é o tratamento adequado de exceções.
James James

6
Concordo que você não deve seguir algumas orientações de maneira sutil, sem uma boa razão, mas discordo do fato de que você deve ter mais de uma classe em um arquivo. Para mim, o arquivo deve ser nomeado após a classe e não conter mais de um. Alguns idiomas (java sendo um) fazem uma observação de realmente impor que a classe esteja em um arquivo com o nome de arquivo correspondente ao nome da classe. Para mim, torna mais fácil a navegação dos novatos, por isso acredito que a classe por arquivo é a coisa certa a fazer
krystan honor

3
Manter uma classe por arquivo e manter o nome do arquivo e o nome da classe sincronizados é uma convenção, assim como nomear variáveis. O Visual Studio está muito bem ajustado para essa abordagem. É mais fácil para sistemas de controle de origem e desenvolvedores adicionados no meio do projeto. Eles procurarão intuitivamente o nome do arquivo que corresponde ao nome da classe.
Oybek

75
static bool GeneralRuleShouldBeFollowed(IGeneralRule rule, IUseCase useCase)
{
    return (rule.Overhead(useCase) 
            < My.PersonalThresholds.ConformismVsPracticality);
}

4
Existe uma regra como essa no .NET? Eu também teria retornado o resultado da declaração. : O
Joan Venge

50

Às vezes, agrupo mais de uma classe dentro de um arquivo se elas estiverem bem acopladas e pelo menos uma delas for muito pequena.

A 'melhor prática' geral é ter um arquivo por classe.


7
Se você reconhece que eles estão acoplados, por que você não os está dissociando?
The Matt

39
@ The Matt: É chamado de evitar a superengenharia. Se você tiver algumas classes fortemente acopladas e dissociá-las, acrescentaria muita complexidade em comparação à quantidade de flexibilidade prática que ela oferece, então qual é o objetivo?
dsimcha

9
Às vezes, tenho basicamente classes de "dados" que são usadas apenas por essa classe. Por isso, geralmente coloco a classe de dados no mesmo arquivo que o usuário, porque o datac-lass geralmente tem pouca ou nenhuma lógica e parece um desperdício para criar um novo arquivo para ele.
Earlz 12/03/10

3
@ The Matt: Às vezes, existem classes auxiliares que eu argumentaria como aceitáveis ​​por acoplamento. Eles reduzem a complexidade de parte de outra classe, mas ainda facilitam um propósito muito específico para essa classe.
Jordan Parmer

6
@TheMatt: dissocie isso: msdn.microsoft.com/en-us/library/… O acoplamento entre módulos é ruim, mas a colaboração entre classes dentro de um recurso lógico é inevitável.
Ben Voigt

25

Além de argumentos hipotéticos e focando no Windows .NET com o Visual Studio IDE e em projetos de software em expansão, faz sentido nesse contexto ter uma classe por arquivo.


Em geral, para referência visual, nada supera uma classe por arquivo. Realmente.

Não sei se a Microsoft faz ou não faz o mesmo, no entanto, eles criaram a partialpalavra - chave para dividir uma classe em vários arquivos (isso é ainda mais grave). É frequentemente usado para dividir o código de designer gerado automaticamente do seu código personalizado na mesma classe (mas às vezes é usado para permitir que diferentes desenvolvedores trabalhem na classe ao mesmo tempo por meio de arquivos diferentes). Portanto, a Microsoft vê os benefícios de vários arquivos e todo mundo tem em mente várias idéias de organização de arquivos com certeza no .NET.

Para classes aninhadas, você não tem escolha a não ser usar um arquivo ou pelo menos as primeiras partes das classes. Um arquivo é necessário e bom neste caso:

class BicycleWheel {
    class WheelSpoke {
    }
}

Caso contrário, por que você manteria várias classes em um arquivo? O argumento "porque eles são pequenos" ou associados um ao outro não retém muita água porque, eventualmente, suas classes serão associadas a outras classes. Por fim, você não pode inferir facilmente a organização de objetos no arquivo com base no uso deles, especialmente à medida que o software continua a crescer.

Além disso, se você usar pastas para espaços para nome , nunca terá um conflito de nome de arquivo de classe. Também é conveniente localizar uma classe por nome de arquivo no sistema de arquivos quando não estiver em um ambiente de desenvolvimento como o Visual Studio (por exemplo, se você deseja editar rapidamente uma classe com o Bloco de Notas ou algo rápido / leve ).

Tantas boas razões ...


Obrigado, o que você quer dizer com "pelo menos as primeiras partes deles"? Quer dizer que você também pode separar classes aninhadas?
Joan Venge 12/03/10

1
Essa é uma referência indireta à partialpalavra - chave que mencionei.
John K

1
Para o registro, as classes aninhadas podem ser partial msdn.microsoft.com/en-us/library/wa80x488(VS.80).aspx . Procurei isso por curiosidade.
John K

Isso mostra apenas que a exibição padrão deve ser lógica (namespace / classe) e não física (arquivos).
David Schmitt

@DavidSchmitt é para isso que serve o Class View no Visual Studio.
Zack

14

Na grande maioria dos casos, sigo a regra de uma classe por arquivo. A única exceção que faço regularmente é a definição de uma enumeração fortemente acoplada a uma classe específica. Nesse caso, frequentemente incluirei a definição da enumeração no arquivo dessa classe.


12

Eu também acredito que deve haver um tipo incluído em um único arquivo.

Há uma exceção a esta regra que deve ser mencionada: Ter duas classes que diferem apenas por um argumento genérico, como:

RelayCommand     

e

RelayCommand<T>

Você conhece a solução alternativa para deixar o StyleCop feliz neste caso?
George Polevoy

Discordo, há outra convenção para classes genéricas, Foo 1.cs for Foo <T> `e Foo 2.cs for Foo <T, T1>`.
Oybek

@Oybek: E se eu tiver Foo <T>, Foo <U> e Foo <U, T>? O que agora?
Andrei Rînea 28/10

@AndreiRinea É impossível ter Foo<T>e Foo<U>dentro do mesmo espaço para nome. Mas se os namespaces forem diferentes, eles geralmente estão em pastas diferentes. Assim, para Foo<T>e Foo<U>deve ser Foo 1.cs and for Foo <U, T> `Foo`2.cs. Mas ainda uma classe por arquivo.
Oybek

Obrigado pela informação! :)
Andrei Rînea

8

Realmente, isso se resume à preferência pessoal. Todo mundo vai dizer "uma classe por arquivo", mas todos temos nossas razões para evitar isso em determinadas circunstâncias. Eu costumava ter um grande projeto que tinha cerca de 300 enums diferentes. De jeito nenhum vou ter 300 arquivos separados, um para cada classe, quando algumas das enumerações eram apenas de três estados.

Também para pessoas que não conseguem encontrar determinadas classes se elas não estiverem em todos os arquivos com o mesmo nome, existe um motivo para você não usar o Find procurando em toda a solução? O uso de Localizar economiza um tempo valioso rolando pelo Solution Explorer.


Re: find - quando estou procurando a definição de um tipo, não quero ver onde ele é usado. Além disso, o find leva significativamente mais tempo do que percorrer uma lista alfabética de arquivos que provavelmente já dividi (por espaço de nome).
22810 Jeff Sternal

1
Percebo que você indicou que está trabalhando com algo que dividiu por espaço de nome. Infelizmente, não temos toda a sorte de trabalhar sempre com projetos que conseguimos gerenciar.
Jason M

6

Não importa quão leve seja o conteúdo, acho que uma classe / interface / etc. por arquivo é essencial.

Se estou trabalhando em uma grande solução no Visual Studio, quero poder ver os arquivos e não ter que me aprofundar para ver. Mesmo com ferramentas de navegação como ReSharper, eu quero um mapeamento 1: 1.

Se você encontrar muitos arquivos de origem com pouco ou nenhum conteúdo (talvez estendendo uma classe, mas não adicionando nada a ela), talvez deva repensar seu design.


5

Acho que agrupar uma classe com sua classe padrão de fábrica no mesmo arquivo é muito útil.


5

Eu normalmente teria uma classe por arquivo, mas você normalmente teria que usar sua discrição para ver se o arquivo poderia conter classes relacionadas, por exemplo, agrupando suas exceções que podem ser reutilizadas por você e por outros desenvolvedores. Nesse caso, o usuário precisa apenas de um arquivo para ser incluído, em vez de vários arquivos.

Portanto, o ponto é: a discrição deve ser usada !!!


1
Verdadeira sabedoria, nenhuma regra na programação é difícil e rápida.
ChaosPandion

4

Em soluções maiores, acho muito valioso ter uma classe por arquivo e que o arquivo tenha o mesmo nome que a classe. Torna muito mais fácil localizar o código em que você precisa trabalhar.


Acho esse argumento menos valioso com ferramentas como ReSharper. Eu faço CTRL + T e começo a digitar o nome do tipo que estou procurando.
Mark

3
Sim, mas você deseja que sua estrutura de aplicativos dependa de uma ferramenta de terceiros?
magnus

1
@magnus Eu não estava dizendo que isso não é uma razão para não fazê-lo, mas um argumento menos convincente para alguém fazer isso.
Mark

4

A ferramenta StyleCop para C # possui regras padrão que requerem não mais que uma classe de nível superior em um espaço para nome (mais qualquer número de interfaces, delegados e enumerações nesse espaço para nome).

Nos casos de duas ou mais classes em que a segunda e as classes subseqüentes são usadas apenas pela primeira, elas podem e devem ser classes internas, visíveis apenas para a classe consumidora.


3
Quando você diz "não mais do que uma classe de nível superior em um espaço para nome", você quer dizer em um namespacebloco, certo?
Daniel Pryden

1
As regras mencionadas são SA1403: documento AC # pode conter apenas um único espaço para nome. SA1402: O documento AC # pode conter apenas uma única classe no nível raiz, a menos que todas as classes sejam parciais e sejam do mesmo tipo.
Steve Gilham

4

Em algum momento uma classe por arquivo, mas ...

Quando várias classes estão estritamente relacionadas, mais de uma classe no mesmo arquivo de origem é, IMHO, MELHOR do que dedicar um arquivo de origem curto a cada classe. A fonte é mais legível e compacta (e usando #region a mesma fonte pode ser mais estruturada do que antes).

Considere também que, às vezes, é NECESSÁRIO espalhar a mesma classe por arquivos diferentes (usando parcial ), pois ter um arquivo de origem com mais de 20.000 linhas não é útil, mesmo com a RAM que tenho disponível (mas essa é outra pergunta).


Obviamente, você deve tentar evitar ter uma classe com tantas linhas de código. Eu diria que até 1000 está ficando muito grande. Também sei que isso nem sempre é possível no código legado.
Peter

Se você tentar gerar código-fonte (meu caso é a geração de ligação de C # para OpenGL a partir de sua especificação, com milhares de pontos de entrada), é difícil pensar em uma pequena classe para uma grande quantidade de dados ...
Luca,

3

Ocasionalmente, deixarei uma classe pequena com uma classe maior, mas apenas se eles estiverem muito relacionados, como um objeto e sua classe de coleção ou fábrica.

Há um problema com isso, no entanto. Eventualmente, a classe pequena cresce até o ponto em que deveria estar em seu próprio arquivo. Se você a move para o novo arquivo, perde fácil acesso ao seu histórico de revisões.

ie

  • na segunda-feira, faço uma alteração nas minhas classes x e y no arquivo y.css
  • na terça-feira, eu separo a classe x em seu próprio arquivo x.css porque cresceu para grande
  • na quarta-feira, meu chefe quer ver o que eu mudei na classe x na segunda-feira, para que ele analise o histórico do x.css, apenas o x.css não mostra o histórico antes das alterações de terça-feira.

1
E se a "classe pequena" for algo como um derivado do EventArgs, destinado a passar informações aos destinatários dos eventos da sua classe? O código seria mais limpo ou mais fácil de manter, movendo-se para outro arquivo? Pode-se argumentar que tal coisa deveria ser uma classe pública aninhada, mas essas também parecem desaprovadas. Quanto ao histórico de revisões, a classe não teria aparecido do nada no changelog e não deveria haver um comentário na nota de código de onde veio?
precisa

Eu classificaria isso como uma very tightly relatedclasse e deixaria em, desde que ocupe apenas uma pequena quantidade de espaço na tela e não seja usado para a mesma finalidade por qualquer outra classe.
gingerbreadboy

3

Isso é realmente um problema? :)
Classes muito pequenas, como enums, podem ser reunidas com outras pessoas. Há uma regra a seguir: reunir apenas classes que tenham algo em comum.

Como digressão - em um dos meus projetos, tenho um arquivo com 150 classes dentro. O arquivo possui 10000 linhas de código. Mas é gerado automaticamente, por isso é totalmente aceitável :)


1
Eu tenho um mesmo arquivo com 120 classes e 750 subclasses com meio milhão de linhas, também é gerado automaticamente. O problema é: se eu clicar nele, devo reiniciar, porque tudo para.
Behrooz

3

Um dos motivos para colocar várias classes relacionadas em um arquivo é que o pobre coitado que usa sua API não precisa gastar meio dia digitando a declaração da importação e o pobre coitado que precisa manter o código não precisa gastar metade um dia percorrendo o clichê da declaração de importação. Minha regra geral é que várias classes pertencem ao mesmo arquivo se você quase sempre usasse um grande subconjunto delas ao mesmo tempo, em vez de apenas uma de cada vez.


1
O que você quer dizer com "clichê de declaração de importação"? "Usando instruções"? Mas as classes não estarão no mesmo espaço para nome?
Joan Venge 12/03/10

3
@Joan: Quero dizer, ter que importar 15 módulos diferentes, mas relacionados, para realizar algo realmente simples. Talvez não se aplique especificamente ao C #. Eu realmente não sei C #, mas em outros idiomas é um problema bastante chato.
dsimcha

Obrigado sim, é por isso que eu estava confusa.
Joan Venge

2

Eu faço isso, mas apenas quando as classes são relacionadas de maneira pai-filho e as classes filho são usadas SOMENTE pelo pai.


Obrigado, mas por que você não o separa em outro arquivo? Apenas curioso.
Joan Venge 12/03/10

@henchman disse isso muito mais eloquentemente do que eu, especialmente quando o objeto filho é muito pequeno. É usally o caso de que a classe filha é propriedades somente com talvez apenas um pouco de lógica
CResults

2

Eu costumo ficar com uma classe por arquivo. Mas farei exceções para grupos de construções semelhantes usadas em todo o projeto. Por exemplo:

  • Um EventArgs.cs que contém todas as EventArgssubclasses, pois geralmente elas têm apenas de 5 a 10 linhas de código, mas geralmente são usadas por várias classes diferentes. Como alternativa, posso colocar as EventArgsclasses no mesmo arquivo da classe que declara os eventos.
  • Um Delegates.cs que contém delegados que são usados ​​em todo o projeto, pois eles geralmente têm apenas 1 linha cada. Novamente, a alternativa é colocá-los no mesmo arquivo da classe que os expõe / consome.
  • Um Enums.cs que contém enums usados ​​em todo o projeto. (Se houver um enumque é usado apenas por uma classe, geralmente eu vou privatepara essa classe.)

2

Outro voto para uma classe por arquivo, sendo o nome do arquivo igual à classe. Para mim, ajuda na manutenção de longo prazo. Eu posso facilmente examinar o repositório e ver quais classes fazem parte de uma solução sem precisar abrir o projeto ou qualquer um dos arquivos.


2

Eu o sigo 99% do tempo. É bom seguir os padrões, mas também acredito que a flexibilidade tem seu lugar. Às vezes, parece uma perda de tempo boba separar as coisas. Naquela época, eu me superava e apenas escrevia meu código.


2

As respostas até agora parecem girar em torno das exceções das pessoas à regra, então aqui está a minha: eu mantenho as classes e as classes de 'amigo' dos metadados juntas ao usar o pacote DataAnnotations no .NET3.5 SP1. Caso contrário, eles estão sempre em arquivos separados. Você sabe, na maioria das vezes. Exceto quando não estão.


2

Eu só faço isso raramente. Por exemplo, se houver uma enumeração ou estrutura intimamente relacionada à classe, mas trivial demais para ser separada por si mesma.

Ou uma classe separada para conter alguns métodos de extensão para essa classe principal.


1

One case could be:quando suas aulas formam um conjunto module / unitque serve algumas classes principais como helper classes, caso contrário, não .

dê uma olhada no código-fonte do projeto ASP.NET MVC 2.0 . Segue estritamente esta regra


1

Gosto da ideia de criar classes menores e garantir que a classe esteja fazendo apenas o que deve fazer. Se você tem várias classes que estão contribuindo para resolver um único problema, não há mal algum em reuni-las no mesmo arquivo.

Eu não seguiria as práticas da EM, pois elas não são as MELHORES PRÁTICAS!


1

Outro ponto que eu não vi mais ninguém mencionar é que, quando você desenvolve o uso da regra de uma classe por arquivo, pode ver facilmente o que essa classe específica está usando.

Por exemplo: você pode ter duas classes em que uma classe usa Linq e a outra não.

Se essas classes estivessem no mesmo arquivo, você não saberia dizer sem examinar o código de qual classe usa o quê. Quando se trata de uma classe por arquivo, basta olhar para a parte superior do arquivo para ver exatamente o que está sendo usado nessa classe. Ajuda se você estiver migrando para uma nova biblioteca, etc.


0

Outro motivo para uma classe por arquivo que não foi mencionado nas respostas postadas até agora é que uma classe por arquivo facilita a compreensão do impacto de um PR durante uma revisão de código. Também reduz conflitos de mesclagem.

Quando alguém publica um PR para obter feedback, posso ver a lista de arquivos alterados e ver imediatamente qualquer sobreposição com o que eu possa estar trabalhando. Dependendo da sobreposição, talvez eu queira examinar o código mais profundamente ou dar um OK, porque estou bastante confiante de que não afetará minhas próprias alterações.

Quando duas pessoas estão trabalhando em um arquivo de várias classes e as duas adicionam uma dependência, há uma boa chance de você ter um conflito de mesclagem no usingbloco na parte superior. A separação das classes em arquivos separa as dependências para que você possa ver o que cada classe está usando e não tenha conflitos como este.

Existem exceções a essa regra (interface + implementação, enumerações, ...), mas é um ponto de partida melhor que o oposto, que normalmente permite que desenvolvedores juniores agrupem todo tipo de classes não relacionadas no mesmo arquivo.

Uma classe por arquivo é uma regra clara e inequívoca, não sujeita a interpretação.


As classes relacionadas em um arquivo estão sujeitas a preferências e interpretações pessoais (como você pode ver em todas as outras respostas aqui sobre quando está tudo bem em usar) e, portanto, é uma regra ruim.


-1

As idas e vindas foram muito interessantes e aparentemente inconclusivas, embora minha impressão geral seja que um mapeamento 1-1 entre classes e arquivos seja a opinião da maioria, embora com algumas exceções de pessoa a pessoa.

Estou curioso para saber se alguma das suas respostas varia dependendo de você: (1) desenvolver um aplicativo Windows Forms, um aplicativo Web, uma Biblioteca ou o que for; ou (2) usando o Visual Studio ou não. Ao usar o VS, parece que a regra de uma classe por arquivo também implicaria uma classe por projeto do VS, uma vez que o consenso em outros segmentos parece ser que as soluções / projetos do VS devem ser espelhados na nomeação e estrutura do diretório / arquivo. De fato, minha impressão é que o consenso é ter o nome do projeto = nome do assembly = nome do espaço para nome (aninhado), os quais seriam espelhados na estrutura e nomeação de diretório / arquivo. Se essas são as diretrizes (ou regras) corretas, todos esses mecanismos de organização aparentemente ortogonais seriam mantidos em sincronia.


-1

Sim, para facilitar a leitura, devemos ter um arquivo por classe !. Apenas pulou em um projeto. Eu vejo muitas classes em um arquivo. apenas torna tão difícil para um cara novo entender isso. não devemos pensar em manutenção? quando desenvolvemos um software? muitas vezes o desenvolvimento continuará por outros desenvolvedores. Temos namespaces para organizar nossas coisas, não precisamos de arquivos para isso !.


-5

Um item de código por arquivo, sim.

Tudo o resto é uma má prática - e, francamente, um sinal de vítima da RAD.

Assim que se inicia o desenvolvimento adequado de software (IoC, padrões de design, DDD, TDD, etc ...) e deixa o campo "omg, vamos fazer isso, não faço ideia de como, mas sou pago", verá que essa regra realmente é realmente importante.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.