Como Anders diz , em parte se trata de desempenho e, em parte, de bloquear projetos mal-pensados para reduzir o escopo de problemas causados posteriormente por pessoas que herdam cegamente coisas que não foram projetadas para serem herdadas.
O desempenho parece óbvio - embora a maioria dos métodos não seja notada no hardware moderno, um getter virtual pode causar um impacto notável, mesmo no hardware moderno que depende cada vez mais de instruções facilmente previstas no pipeline da CPU.
Agora, o motivo de tornar tudo virtual por padrão parece ser motivado apenas por uma coisa: estruturas de teste de unidade. Parece-me que este é um motivo fraco, em que estamos alterando o código para se adequar à estrutura, em vez de projetá-la adequadamente.
Talvez o problema seja com as estruturas usadas aqui, elas devem melhorar para permitir a substituição de funções em tempo de execução por calços injetados, em vez de criar código que faça isso (com todos os hacks para contornar métodos privados e não virtuais, para não mencionar funções estáticas e de terceiros)
Portanto, a razão pela qual os métodos não são virtuais por padrão é como Anders disse, e qualquer desejo de torná-los virtuais para se adequar a alguma ferramenta de teste de unidade está colocando o carrinho à frente do cavalo, o design adequado supera as restrições artificiais.
Agora você pode mitigar tudo isso usando interfaces ou reformulando toda a sua base de código para usar componentes (ou microsserviços) que se comunicam por meio da passagem de mensagens em vez de chamadas de método com fio direto. Se você aumentar a superfície de uma unidade para que o teste seja um componente e esse componente seja totalmente independente, você não precisará realmente parafusá-lo para testá-lo.