Por que usar classes parciais?

72

No meu entender, a partialpalavra - chave não faz nada além de permitir que uma classe seja dividida entre vários arquivos de origem. Existe algum motivo para fazer isso além da organização do código? Eu já vi isso usado nas classes geradas da interface do usuário.

Parece um mau motivo para criar uma palavra-chave inteira. Se uma classe é grande o suficiente para exigir vários arquivos, provavelmente está fazendo muito. Eu pensei que talvez você pudesse usá-lo para definir parcialmente uma classe para outro programador em algum lugar concluir, mas seria melhor criar uma classe abstrata.

Michael K
fonte
10
Também não é realmente uma "palavra-chave inteira". É uma palavra-chave contextual . partialsó significa algo quando vem antes class. Você pode usá-lo como um nome de identificador em outras partes do código, etc.
Dean Harding
4
Se você os usar, e não for para código gerado, eu odeio você. Você não pessoalmente, mas geralmente. Eu odeio caçar código através de classes parciais.
21411 Steven Stevers
3
Não é difícil "caçar código", todos os métodos de classe ainda aparecem no menu suspenso no VS, independentemente de qual parte da parcial você está vendo. Outro navegação de código funciona muito bem também (ou navegação "inteligente" R # estilo ou bom e velho shift-ctrl-f
Steve
2
É um mal-entendido que as classes parciais precisam estar em arquivos separados.
Bart

Respostas:

110

É muito útil em todos os cenários em que uma parte da classe é gerada por alguma ferramenta personalizada, pois permite adicionar lógica personalizada ao código gerado sem herdar a classe gerada. Btw. também existem métodos parciais pela mesma razão.

Não se trata apenas da interface do usuário, mas também de outras tecnologias, como o Linq-To-Sql ou o Entity Framework, que usam muito isso.

Ladislav Mrnka
fonte
44
Além disso, permite manter seu código sob controle de versão, deixando o código gerado fora do controle de versão.
Frank Shearar
3
Bom ponto, nunca pensei nisso. Eu só vi isso abusado na prática. Uma maneira de programadores (ruins) manterem o tamanho do arquivo gerenciável.
Martin Wickman
11
Os melhores exemplos que usei são: a) para contextos de dados Linq to SQL nos quais você deseja estender as classes; b) estender classes passadas de serviços da web. Em nenhum dos casos é este um abuso nem é um meio para manter tamanhos de arquivo gerenciáveis ... eu seria radicalmente infeliz para vê-lo usado dessa maneira (e iria tomar medidas ...)
Murph
3
As classes do WinForm o usam para ocultar todos os controles de criação de código do designer (especialmente importantes porque o designer gosta de reordenar aleatoriamente todo o código que cria, resultando em grandes bagunças ao se difundir / culpar) e, se você estiver trabalhando com XML, a ferramenta XSD pode crie classes parciais fora do arquivo de esquema novamente, permitindo que você separe seu código da classificação gerada automaticamente.
Dan Neely
2
@MartinWickman não necessariamente ruim. É um bom ponto de partida para refatorar objetos de Deus no código legado. Tente limpar a paisagem dividindo primeiro grandes classes em arquivos separados. (Eu sei que o seu comentário é muito antiga)
Konrad Morawski
26

Como você diz, é frequentemente usado para separar o código gerado. Geralmente, nada tem a ver com o tamanho das classes / arquivos.

O benefício de separar o código gerado é um estilo. O código gerado pode ser bastante feio e ilegível e falharia em muitos padrões de codificação (e nas verificações do StyleCop), mas tudo bem, ninguém precisa lê-lo ou mantê-lo diretamente. Portanto, se você o "ocultar" em outro arquivo, poderá se concentrar em garantir que o restante da classe esteja no padrão, seja aprovado nas verificações do StyleCop e assim por diante.

Outra área em que eu usei é onde uma classe implementa várias interfaces; pode ser muito bom separar a implementação em arquivos separados, embora isso seja mais uma questão de preferência pessoal, nunca vi nenhum padrão de codificação exigir ( ou impedir) isso.

Steve
fonte
9
Eu nunca pensei em usar parciais de classe para separar implementações de interface, é uma excelente idéia.
Mark Booth
É muito mais que estilo. Os desenvolvedores de um gerador de código teriam que passar por etapas para permitir que você editasse o código em um arquivo gerenciado pelo gerador, e mesmo assim seria problemático. O benefício número 1, para mim, é fornecer um local para escrever um código que o gerador irá / não pode tocar. Este é um benefício bastante tangível.
Daniel
19

Um dos desenvolvedores em que trabalho surgiu bastante útil para eles, há algumas semanas, na refatoração de grandes classes de Deus que ficaram fora de controle e têm muitos métodos públicos: separando as funções lógicas de cada parte do a classe em classes parciais separadas, é possível separar fisicamente a classe nas unidades mais atômicas que devem ser as classes sem interromper nenhuma funcionalidade existente, permitindo ver o que é comum e o que não é. Com isso como um primeiro estágio, é possível dividir mais facilmente as parciais em suas próprias classes independentes e implementá-las em toda a base de código. Eu pensei que era uma boa ideia.

No entanto, em geral, acho que eles devem ser usados ​​apenas para aumentar as classes geradas por máquina ao escrever um novo código.


fonte
11
Eu posso ver como isso pode ser útil. Se isso é justificativa para estar no idioma ou não ... eu não sei. Código gerado por máquina é outra história, no entanto.
Michael K
2
Sim, não é uma justificativa para estar no idioma, mas é uma maneira interessante de usá-los.
18

Posso pensar em alguns cenários úteis em que parciais fazem sentido, a maioria deles estou me usando em meus projetos:

  • Para separar o código gerado pelo Tool / IDE / Designer do código que você está mantendo. Um bom exemplo é o Form.Designer.csarquivo que contém o código gerado pelo designer para aplicativos de formulários do Windows. Muitos outros formatos .NET também possuem algum código gerado por ferramenta, que pode ser potencialmente regenerado quando você constrói seu projeto e, portanto, todas as suas alterações personalizadas serão eliminadas. A separação ajudará você a manter seu código e alterações protegidos contra essas modificações automatizadas.

  • Quando você está implementando várias interfaces com muito código na implementação. Eu costumo usar um arquivo parcial separada para cada interface, nomeando-o assim: {Class}.{Interface}.cs. É fácil para mim ver no IDE quais interfaces o {Class}está implementando e como.

  • Quando a classe deve conter uma ou mais classes aninhadas , especialmente com código suficiente para ser colocada em um arquivo separado. Eu manteria o padrão acima e usaria a {Class}.{NestedClass}.csconvenção de nomenclatura para cada classe aninhada. Prática semelhante já é mencionada pela resposta do Virtlink .

  • Quando escrevo staticclasses que conterão métodos de extensão . Geralmente, é o caso de fornecer a mesma lógica do método de extensão para classes ou interfaces semelhantes - como, por exemplo, Reverseem coleções genéricas e não genéricas. Eu colocaria todos os métodos de extensão para uma única classe ou interface em uma parcial separada da classe estática. Por exemplo, eu teria todos os métodos de extensão da IListinterface em um só lugar e os mesmos métodos usados IList<T>em outro arquivo. Outra abordagem seria colocar o mesmo método (todas as suas sobrecargas) na mesma parcial, com todas as classes possíveis para o thisparâmetro - como ter todas asReverseimplementações em um arquivo. Depende de qual justificaria melhor a separação em termos de volume de código ou de algumas convenções internas às quais você ou sua organização podem estar aderindo.

  • Eu não uso isso, mas já vi algumas pessoas de C / C ++ gostarem da abordagem que descreverei aqui: crie uma parcial para a classe apenas com métodos parciais . Isso se assemelha à maneira C / C ++ para definir interfaces e separar a declaração do método da implementação.

  • Separação por preocupações puramente lógicas . Se você trabalhar com uma classe grande que combine mais de um conjunto lógico de operações, poderá separar cada código relacionado à lógica em uma parcial separada. Normalmente, a existência de tais classes é contrária ao princípio da separação de preocupações , mas é frequentemente observado o caso da vida real, especialmente para bases de código mais antigas. O usuário Steve Evers os mencionou em sua resposta a esta pergunta , referindo-se a eles pelo nome god objects. Eu pessoalmente usaria a abordagem de classes parciais para dividir o código antes que ocorra qualquer refatoração de arquivos realmente grandes, a fim de facilitar meu trabalho e tornar a refatoração mais transparente. Isso também reduzirá os conflitos que potencialmente causarei quando sistemas de versão como o SVN estiverem envolvidos.

Ivaylo Slavov
fonte
14

Eu nunca vi isso mencionado por ninguém: eu uso partialpara colocar classes aninhadas em seus próprios arquivos.

Todos os meus arquivos de código contêm apenas uma classe, estrutura, interface ou enumeração. Torna muito mais fácil encontrar a definição de um objeto quando os nomes dos arquivos mostram o nome da coisa que você está procurando. E como o Visual Studio tenta corresponder as pastas do projeto aos espaços para nome, os nomes dos arquivos devem corresponder às classes.

Isto também significa que uma classe NestedClassaninhada dentro MyClassterá seu próprio arquivo em meus projetos: MyClass.NestedClass.cs.

partial class MyClass
{
    private class NestedClass
    {
        // ...
    }
}
Daniel AA Pelsmaeker
fonte
11
Eu me pergunto por que alguém votaria negativamente nesta resposta. A prática mencionada não é um antipadrão, nem causaria problemas que eu conheça.
Ivaylo Slavov
11
Isso também soluciona o problema de manter os arquivos de classe pequenos (em termos de linhas de código) e ainda permitir o encapsulamento adequado do código que pertence a uma classe interna.
redcalx
10

Com a exceção de usar código gerado, eu só os vi usados ​​em um esforço para encobrir objetos divinos . Tentar entender uma nova base de código ou navegar por vários arquivos de origem, que são o mesmo objeto, é muitíssimo irritante.

Então, quando você pergunta, Why use partial classes?eu respondo: a menos que você esteja usando código gerado, não.

Steven Evers
fonte
+1. É a primeira vez que vejo esses tipos de objetos sendo chamados de nomes (objetos divinos) e é até oficial. Nunca é tarde para aprender algo novo.
Ivaylo Slavov
6

A organização do código é a única razão, mas é mais profunda do que parece à primeira vista. Se você tiver classes parciais nas quais as peças são geradas, poderá facilmente:

  • Gere novamente o código sem precisar detectar alterações manuais nas classes nas quais você escreve (para não substituir essas partes).
  • Exclua as classes parciais geradas da cobertura de teste, controle de origem, métricas etc.
  • Use o código gerado sem forçar você a basear sua estratégia de herança em torno dele (você ainda pode herdar de outras classes).
Deckard
fonte
1

Também existem alguns lugares em que as classes geradas / implícitas são declaradas parciais; portanto, se o Dev precisar estendê-las, elas terão acesso total sem ter que mexer na herança e substituição de todo o local. Observe as classes geradas na área temp do asp.net depois de executar um site de formulários da web pela primeira vez no IIS, se você quiser tentar pegar alguns exemplos.

Conta
fonte
1

Eu posso pensar em um uso sujo para eles.

Suponha que você tenha algumas classes que precisam de funcionalidade comum, mas não deseja injetar essa funcionalidade na cadeia de herança acima delas. Ou você tem um conjunto de classes que usam uma classe auxiliar comum.

Basicamente, você deseja fazer herança múltipla, mas não gosta de C #. Então, o que você faz é usar parcial para criar o que é essencialmente um #include em C / C ++;

Coloque toda a funcionalidade em uma classe ou use a classe auxiliar. Em seguida, copie o ajudante X vezes e renomeie-o como classe parcial A, B, C. e coloque parcial nas suas classes A, B, C.

Disclaimer: Sim, isso é mau. Nunca faça isso - especialmente se isso deixará outras pessoas com raiva de você.

Marca
fonte
Mixins. Isto poderia ser combinado com T4 para evitar cópia manual ...
Peter Taylor
Como observado, isso se parece muito com mixins. No entanto, você está abordando um conceito conhecido como "traços" e eles lidam com segurança com o que você está tentando alcançar. Tenho uma descrição independente do idioma em publius-ovidius.livejournal.com/314737.html Procurei uma implementação limpa para C #, mas não a vi.
Ovid