Por que selar uma classe?

96

Eu gostaria de ouvir qual é a motivação por trás da maioria das classes seladas na estrutura .Net. Qual é a vantagem de selar uma classe? Não consigo entender como não permitir a herança pode ser útil e provavelmente não o único a lutar contra essas classes.

Então, por que a estrutura foi projetada dessa maneira e não seria uma mudança inabalável desmarcar tudo? Deve haver outro motivo, mas apenas ser mau?

mmiika
fonte

Respostas:

41
  • Às vezes, as classes são preciosas demais e não foram projetadas para serem herdadas.
  • O Runtime / Reflection pode fazer suposições de herança sobre classes seladas ao procurar por tipos. Um grande exemplo disso é - Recomenda-se que os atributos sejam lacrados para a velocidade do tempo de execução de pesquisa. type.GetCustomAttributes (typeof (MyAttribute)) terá um desempenho significativamente mais rápido se MyAttribute estiver selado.

O artigo do MSDN para este tópico é Limitando a extensibilidade por classes de vedação .

CVertex
fonte
3
Fico feliz em ver que eles dizem claramente "use com cautela" agora ... gostaria que eles praticassem o que pregam.
mmiika
13
Parece um conselho ruim para mim :(
Jon Skeet
4
@CVertex: Desculpe, não estava tentando criticar você - apenas o artigo.
Jon Skeet
16
@generalt: Eu acredito em projetar para herança ou proibi-lo. Projetar para herança exige um pouco de trabalho e geralmente limitará as implementações no futuro. A herança também introduz incerteza nos chamadores sobre o que exatamente eles farão. Também não combina bem com imutabilidade (da qual sou fã). Eu só acho a herança de classes útil em um número relativamente pequeno de lugares (embora eu adore interfaces).
Jon Skeet
1
@CVertex Se você já usou o .NET, provavelmente se deparou com o problema e simplesmente não percebeu, quase todas as classes do .NET core estão lacradas.
CoryG
103

As classes devem ser projetadas para herança ou proibi-la. Há um custo para projetar para herança:

  • Ele pode definir sua implementação (você deve declarar quais métodos irão chamar quais outros métodos, no caso de um usuário substituir um, mas não o outro)
  • Ele revela sua implementação, e não apenas os efeitos
  • Isso significa que você deve pensar em mais possibilidades ao projetar
  • Coisas como iguais são difíceis de projetar em uma árvore de herança
  • Requer mais documentação
  • Um tipo imutável que é uma subclasse pode se tornar mutável (ick)

O item 17 do Effective Java fornece mais detalhes sobre isso - independentemente do fato de ter sido escrito no contexto do Java, o conselho também se aplica ao .NET.

Pessoalmente, gostaria que as classes fossem lacradas por padrão no .NET.

Jon Skeet
fonte
26
Hmm .. se você estende uma classe, não é seu problema se você interrompê-la?
mmiika
25
E se uma mudança de implementação sobre a qual você não tem controle na classe base quebrar você? De quem é a culpa? A herança introduz fragilidade, basicamente. Favorecer a composição em vez da herança promove robustez, IMO.
Jon Skeet
6
Sim, as interfaces são boas - e sim, você pode favorecer a composição de qualquer maneira. Mas se eu expor uma classe base não lacrada sem pensar muito cuidadosamente sobre ela, devo esperar que as mudanças possam quebrar as classes derivadas. Isso parece uma coisa ruim para mim. Melhor selar a aula e evitar quebra, IMO.
Jon Skeet
8
@Joan: Composição é um relacionamento "tem um" em vez de "é um". Portanto, se você deseja escrever uma classe que pode atuar como uma lista em alguns aspectos, mas não em outros, você pode querer criar uma classe com uma variável de membro List <T>, em vez de derivar de List <T>. Você então usaria a lista para implementar vários métodos.
Jon Skeet
3
@ThunderGr: Mas esse é o meu ponto: quando você não precisa se preocupar com quais outras implementações são fornecidas pelas subclasses, você pode ser mais livre com sua própria implementação. Por exemplo, suponha que um método seja implementado chamando outro método, e ambos são virtuais. Isso precisa ser documentado - embora pareça um detalhe de implementação. Basicamente, projetar para herança adiciona algemas - que são apropriadas em alguns casos, mas não em outros. Eu prefiro ter uma classe lacrada implementando uma interface do que uma classe não lacrada com um monte de métodos virtuais.
Jon Skeet
8

Parece que as diretrizes oficiais da Microsoft sobre vedação evoluíram desde que essa pergunta foi feita há cerca de 9 anos, e mudaram de uma filosofia de aceitação (selar por padrão) para cancelar (não selar por padrão):

X NÃO feche aulas sem ter um bom motivo para fazê-lo.

Selar uma classe porque você não consegue pensar em um cenário de extensibilidade não é um bom motivo. Os usuários do framework gostam de herdar de classes por vários motivos não óbvios, como adicionar membros de conveniência. Consulte Classes não lacradas para obter exemplos de motivos não óbvios pelos quais os usuários desejam herdar de um tipo.

Boas razões para selar uma classe incluem o seguinte:

  • A classe é uma classe estática. Consulte Projeto de classe estática.
  • A classe armazena segredos sensíveis à segurança em membros protegidos herdados.
  • A classe herda muitos membros virtuais e o custo de lacrá-los individualmente superaria os benefícios de deixar a classe sem lacre.
  • A classe é um atributo que requer uma consulta de tempo de execução muito rápida. Os atributos selados têm níveis de desempenho ligeiramente mais altos do que os não selados. Veja Atributos.

X NÃO declare membros protegidos ou virtuais em tipos selados.

Por definição, os tipos selados não podem ser herdados. Isso significa que os membros protegidos em tipos selados não podem ser chamados e os métodos virtuais em tipos selados não podem ser substituídos.

✓ CONSIDERE os membros de selagem que você anular. Os problemas que podem resultar da introdução de membros virtuais (discutidos em Membros virtuais) também se aplicam a substituições, embora em um grau um pouco menor. Vedar uma substituição protege você desses problemas a partir daquele ponto na hierarquia de herança.

Na verdade, se você pesquisar a base de código ASP.Net Core , encontrará apenas cerca de 30 ocorrências de sealed class, a maioria das quais são atributos e classes de teste.

Eu realmente acho que a conservação da imutabilidade é um bom argumento a favor da vedação.

Ohad Schneider
fonte
4

Encontrei esta frase na documentação do msdn: "Classes seladas são usadas principalmente para evitar derivação. Como nunca podem ser usadas como classe base, algumas otimizações de tempo de execução podem tornar a chamada de membros de classe selada um pouco mais rápida."

Não sei se a atuação é a única vantagem das aulas fechadas e pessoalmente também gostaria de saber outros motivos ...

bruno conde
fonte
4
Seria interessante ver de que tipo de benefício de desempenho eles estão falando ...
mmiika
3

O desempenho é um fator importante, por exemplo, a classe da string em java é final (<- selada) e a razão para isso é apenas o desempenho. Acho que outro ponto importante é evitar o problema da classe base frágil descrito em detalhes aqui: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Se você fornecer uma estrutura, é importante para projetos legados de manutenção e atualizar sua estrutura para evitar o problema da classe base frágil

Peter Parker
fonte
A razão para String em java ser final não é desempenho, é segurança.
CesarB
@CesarB: Sim, mas também, String não é uma classe Java normal. É a única (acredito) classe em Java que oferece suporte à sobrecarga de operador (para saber mais, veja aqui , a seção: "Até mesmo C e Java têm sobrecarga de operador (codificado)"), o que não é possível em uma classe normal. Por causa disso, a Stringclasse pode nem ser possível subclassificar, mesmo que não seja final.
wchargin em
1

Selado é usado para evitar o "problema da classe base frágil". Eu encontrei um bom artigo no MSDN que explica isso.

ihebiheb
fonte
0

A vedação permite que você obtenha alguns pequenos ganhos de desempenho. Isso é menos verdadeiro no mundo dos JITs e da pessimização preguiçosa do que no mundo, digamos, do C ++, mas como o .NET não é tão bom quanto a pessimização como os compiladores Java são, principalmente por causa de diferentes filosofias de design, ele ainda é útil. Diz ao compilador que pode chamar diretamente quaisquer métodos virtuais em vez de chamá-los indiretamente por meio da vtable.

Também é importante quando você deseja um 'mundo fechado' para coisas como comparação de igualdade. Normalmente, uma vez que defino um método virtual, fico muito preocupado em definir uma noção de comparação de igualdade que realmente implemente a ideia. Por outro lado, posso ser capaz de defini-lo para uma subclasse específica da classe com o método virtual. Selar essa classe garante que a igualdade realmente se mantenha.

Edward KMETT
fonte
0

Selar uma classe torna o gerenciamento de recursos descartáveis ​​mais fácil.

Jeff Dunlop
fonte
0

Para determinar se deve selar uma classe, método ou propriedade, você geralmente deve considerar os seguintes dois pontos:

• Os benefícios potenciais que classes derivadas podem obter por meio da capacidade de personalizar sua classe.

• O potencial de que classes derivadas podem modificar suas classes de tal forma que elas não funcionem mais corretamente ou conforme o esperado.

usuário 3629577
fonte
0

Outra consideração é que as classes lacradas não podem ser fragmentadas em seus testes de unidade. Da documentação da Microsoft :

Classes seladas ou métodos estáticos não podem ser fragmentados porque os tipos de stub dependem do envio de método virtual. Para tais casos, use os tipos de calços conforme descrito em Usando calços para isolar seu aplicativo de outros conjuntos para teste de unidade

Dave Clark
fonte