Ao contrário do Java, por que o C # trata os métodos como funções não virtuais por padrão? É mais provável que seja um problema de desempenho do que outros resultados possíveis?
Lembro-me de ler um parágrafo de Anders Hejlsberg sobre várias vantagens que a arquitetura existente está trazendo. Mas e os efeitos colaterais? É realmente uma boa troca ter métodos não virtuais por padrão?
c#
java
.net
virtual-functions
Burcu Dogan
fonte
fonte
this
referência for nula. Veja aqui para mais informações.Respostas:
As classes devem ser projetadas para herança para poder tirar proveito disso. Ter métodos
virtual
por padrão significa que cada função da classe pode ser removida e substituída por outra, o que não é realmente uma coisa boa. Muitas pessoas até acreditam que as aulas deveriam ser osealed
padrão.virtual
os métodos também podem ter uma leve implicação no desempenho. No entanto, não é provável que esse seja o motivo principal.fonte
callvirt
por um simplescall
no código IL (ou ainda mais adiante durante o JITting). Java HotSpot faz o mesmo. O resto da resposta é exata.Estou surpreso que parece haver um consenso aqui de que não virtual por padrão é a maneira certa de fazer as coisas. Vou descer do outro lado - acho pragmático - da cerca.
A maioria das justificativas me parece o velho argumento "Se dermos a você o poder, você pode se machucar". De programadores ?!
Parece-me que o codificador que não sabia o suficiente (ou não tinha tempo) para projetar sua biblioteca para herança e / ou extensibilidade é o codificador que produziu exatamente a biblioteca que provavelmente terei de consertar ou ajustar - exatamente o biblioteca onde a capacidade de substituir seria mais útil.
O número de vezes que tive que escrever um código de solução feio e desesperado (ou abandonar o uso e lançar minha própria solução alternativa) porque não posso substituir muito, supera em muito o número de vezes que já fui mordido ( por exemplo, em Java) substituindo onde o designer pode não ter considerado eu.
Não virtual por padrão torna minha vida mais difícil.
ATUALIZAR: Foi apontado [muito corretamente] que eu realmente não respondi à pergunta. Então - e com desculpas pelo atraso ...
Eu meio que queria ser capaz de escrever algo incisivo como "C # implementa métodos como não virtuais por padrão porque uma má decisão foi tomada que valorizava mais os programas do que os programadores". (Acho que isso poderia ser um pouco justificado com base em algumas das outras respostas a esta pergunta - como desempenho (otimização prematura, alguém?), Ou garantia do comportamento das classes.)
No entanto, percebo que estaria apenas declarando minha opinião e não aquela resposta definitiva que Stack Overflow deseja. Certamente, pensei, no nível mais alto, a resposta definitiva (mas inútil) é:
Eles não são virtuais por padrão porque os designers da linguagem tiveram uma decisão a tomar e foi isso que escolheram.
Agora eu acho que a razão exata pela qual eles tomaram essa decisão nós nunca ... oh, espere! A transcrição de uma conversa!
Portanto, parece que as respostas e comentários aqui sobre os perigos de substituir APIs e a necessidade de projetar explicitamente para herança estão no caminho certo, mas estão todos perdendo um aspecto temporal importante: a principal preocupação de Anders era manter uma classe ou APIs implícitas contrato entre versões . E eu acho que ele está realmente mais preocupado em permitir que a plataforma .Net / C # mude no código, em vez de se preocupar com a mudança do código do usuário no topo da plataforma. (E seu ponto de vista "pragmático" é exatamente o oposto do meu porque ele está olhando do outro lado.)
(Mas eles não poderiam simplesmente ter escolhido o virtual por padrão e, em seguida, adicionado "final" por meio da base de código? Talvez não seja exatamente a mesma coisa ... e Anders é claramente mais inteligente do que eu, então vou deixar assim.)
fonte
virtual
... Alguém suplemento do Visual Studio?Porque é muito fácil esquecer que um método pode ser substituído e não projetado para isso. C # faz você pensar antes de torná-lo virtual. Acho que é uma ótima decisão de design. Algumas pessoas (como Jon Skeet) até disseram que as classes deveriam ser seladas por padrão.
fonte
Para resumir o que outros disseram, existem alguns motivos:
1- Em C #, há muitas coisas na sintaxe e na semântica que vêm direto do C ++. O fato de que os métodos não eram virtuais por padrão em C ++ influenciou C #.
2- Ter todos os métodos virtuais por default é uma preocupação de desempenho, pois toda chamada de método deve usar a Tabela Virtual do objeto. Além disso, isso limita fortemente a capacidade do compilador Just-In-Time de usar métodos sequenciais e realizar outros tipos de otimização.
3- Mais importante, se os métodos não são virtuais por padrão, você pode garantir o comportamento de suas classes. Quando eles são virtuais por padrão, como em Java, você não pode nem mesmo garantir que um método getter simples fará conforme o planejado porque ele pode ser substituído para fazer qualquer coisa em uma classe derivada (é claro que você pode, e deve, fazer o método e / ou final da aula).
Alguém pode se perguntar, como Zifre mencionou, por que a linguagem C # não deu um passo adiante e tornou as classes lacradas por padrão. Isso é parte de todo o debate sobre os problemas de herança de implementação, que é um tópico muito interessante.
fonte
C # é influenciado por C ++ (e mais). C ++ não habilita o envio dinâmico (funções virtuais) por padrão. Um (bom?) Argumento para isso é a pergunta: "Com que freqüência você implementa classes que são membros de uma pesquisa de classe?". Outro motivo para evitar a ativação do despacho dinâmico por padrão é a área de cobertura da memória. Uma classe sem um ponteiro virtual (vpointer) apontando para uma tabela virtual , é obviamente menor do que a classe correspondente com a vinculação tardia habilitada.
O problema de desempenho não é tão fácil de dizer "sim" ou "não". A razão para isso é a compilação Just In Time (JIT), que é uma otimização de tempo de execução em C #.
Outra pergunta semelhante sobre " velocidade das chamadas virtuais .. "
fonte
A razão simples é o custo de design e manutenção, além dos custos de desempenho. Um método virtual tem custo adicional em comparação com um método não virtual porque o designer da classe deve planejar o que acontecerá quando o método for substituído por outra classe. Isso tem um grande impacto se você espera que um método específico atualize o estado interno ou tenha um comportamento específico. Agora você deve planejar o que acontece quando uma classe derivada altera esse comportamento. É muito mais difícil escrever código confiável nessa situação.
Com um método não virtual, você tem controle total. Tudo o que der errado é culpa do autor original. O código é muito mais fácil de raciocinar.
fonte
Se todos os métodos C # fossem virtuais, o vtbl seria muito maior.
Os objetos C # só têm métodos virtuais se a classe tiver métodos virtuais definidos. É verdade que todos os objetos têm informações de tipo que incluem um equivalente vtbl, mas se nenhum método virtual for definido, apenas os métodos de base do objeto estarão presentes.
@Tom Hawtin: Provavelmente é mais correto dizer que C ++, C # e Java são todos da família de linguagens C :)
fonte
Vindo de um background de perl, acho que o C # selou o destino de todo desenvolvedor que quisesse estender e modificar o comportamento de uma classe base por meio de um método não virtual, sem forçar todos os usuários da nova classe a estarem cientes dos possíveis bastidores detalhes.
Considere o método Add da classe List. E se um desenvolvedor quisesse atualizar um dos vários bancos de dados em potencial sempre que uma lista específica fosse 'Adicionada'? Se 'Adicionar' fosse virtual por padrão, o desenvolvedor poderia desenvolver uma classe 'BackedList' que substituísse o método 'Adicionar' sem forçar todo o código do cliente a saber que era um 'BackedList' em vez de uma 'Lista' normal. Para todos os efeitos práticos, a 'BackedList' pode ser vista apenas como outra 'Lista' do código do cliente.
Isso faz sentido do ponto de vista de uma grande classe principal que pode fornecer acesso a um ou mais componentes de lista que são apoiados por um ou mais esquemas em um banco de dados. Dado que os métodos C # não são virtuais por padrão, a lista fornecida pela classe principal não pode ser um IEnumerable ou ICollection simples ou mesmo uma instância de List, mas deve ser anunciada ao cliente como um 'BackedList' para garantir que a nova versão da operação 'Adicionar' é chamado para atualizar o esquema correto.
fonte
B
forA
. SeB
requer algo diferente do queA
não éA
. Acredito que substituir como um recurso na própria linguagem é uma falha de design. Se você precisar de umAdd
método diferente , sua classe de coleção não será aList
. Tentar dizer que é isso é fingimento. A abordagem certa aqui é a composição (e não a falsificação). É verdade que toda a estrutura é construída sobre recursos de substituição, mas eu simplesmente não gosto disso.Certamente não é um problema de desempenho. O interpretador Java da Sun usa o mesmo código para despachar (
invokevirtual
bytecode) e o HotSpot gera exatamente o mesmo código, sejafinal
ou não. Eu acredito que todos os objetos C # (mas não structs) têm métodos virtuais, então você sempre vai precisar davtbl
identificação de classe / runtime. C # é um dialeto de "linguagens semelhantes a Java". Sugerir que vem do C ++ não é totalmente honesto.Existe uma ideia de que você deve "projetar para herança ou então proibi-lo". O que parece uma ótima ideia até o momento em que você tem um caso sério de negócios para resolver rapidamente. Talvez herdando de código que você não controla.
fonte
final
efinal
métodos eficazes é trivial. Também é importante notar que ele pode fazer inlining bimórfico, ou seja, métodos inline com duas implementações diferentes.