Por que o C # foi projetado dessa maneira?
Pelo que entendi, uma interface apenas descreve o comportamento e serve para descrever uma obrigação contratual para as classes que implementam a interface de que determinado comportamento é implementado.
Se as classes desejam implementar esse comportamento em um método compartilhado, por que não deveriam?
Aqui está um exemplo do que tenho em mente:
// These items will be displayed in a list on the screen.
public interface IListItem {
string ScreenName();
...
}
public class Animal: IListItem {
// All animals will be called "Animal".
public static string ScreenName() {
return "Animal";
}
....
}
public class Person: IListItem {
private string name;
// All persons will be called by their individual names.
public string ScreenName() {
return name;
}
....
}
c#
oop
language-features
Kramii
fonte
fonte
IListItem.ScreenName() => ScreenName()
(usando a sintaxe C # 7) implementará o método da interface explicitamente chamando o método estático. As coisas ficam feias quando você adiciona herança a que, embora (você tem que reimplementar a interface)Respostas:
Supondo que você esteja se perguntando por que não pode fazer isso:
Isso não faz sentido para mim, semanticamente. Os métodos especificados em uma interface devem estar lá para especificar o contrato para interagir com um objeto. Os métodos estáticos não permitem que você interaja com um objeto - se você estiver na posição em que sua implementação pode ficar estática, talvez seja necessário se perguntar se esse método realmente pertence à interface.
Para implementar seu exemplo, eu daria ao Animal uma propriedade const, que ainda permitiria que ele fosse acessado de um contexto estático, e retornaria esse valor na implementação.
Para uma situação mais complicada, você sempre pode declarar outro método estático e delegar a ele. Ao tentar criar um exemplo, não consegui pensar em nenhum motivo para você fazer algo não trivial, tanto no contexto estático quanto no de instância, por isso pouparei um blob FooBar e tomo isso como uma indicação de Não seja uma boa ideia.
fonte
public static object MethodName(this IBaseClass base)
dentro de uma classe estática. A desvantagem, no entanto, é que, diferentemente de uma herança de interface - isso não força / permite que os herdeiros individuais substituam bem a metodologia.Meu motivo técnico (simplificado) é que os métodos estáticos não estão na vtable e o site de chamada é escolhido no momento da compilação. É o mesmo motivo pelo qual você não pode ter membros de substituição ou estáticos virtuais. Para mais detalhes, você precisaria de um graduado em CS ou de um compilador - dos quais eu também não sou.
Pela razão política, cito Eric Lippert (que é um compilador experiente e é bacharel em Matemática, Ciência da Computação e Matemática Aplicada pela Universidade de Waterloo (fonte: LinkedIn ):
Observe que Lippert deixa espaço para o chamado método de tipo:
mas ainda está para ser convencido de sua utilidade.
fonte
object
e como eles têm permissão para implementar interfaces.A maioria das respostas aqui parece não entender o ponto. O polimorfismo pode ser usado não apenas entre instâncias, mas também entre tipos. Isso geralmente é necessário quando usamos genéricos.
Suponha que tenhamos o parâmetro type no método genérico e precisamos fazer alguma operação com ele. Não queremos instanciar, porque desconhecemos os construtores.
Por exemplo:
Infelizmente, só posso encontrar alternativas "feias":
Use a reflexão Ugly e supera a idéia de interfaces e polimorfismo.
Crie classe de fábrica completamente separada
Isso pode aumentar bastante a complexidade do código. Por exemplo, se estamos tentando modelar objetos de domínio, cada objeto precisaria de outra classe de repositório.
Instancie e chame o método de interface desejado
Isso pode ser difícil de implementar, mesmo se controlarmos a fonte das classes, usada como parâmetros genéricos. O motivo é que, por exemplo, podemos precisar que as instâncias estejam apenas no estado conhecido "conectado ao banco de dados".
Exemplo:
para usar a instanciação para resolver o problema da interface estática, precisamos fazer o seguinte:
Isso é obviamente feio e também desnecessário complica o código para todos os outros métodos. Obviamente, também não é uma solução elegante!
fonte
public abstract class DBObject<T> where T : DBObject<T>, new()
e depois fazer todas as classes de banco de dados herdarem comoDBObject<T>
. Em seguida, você pode transformar a recuperação por chave em uma função estática com o tipo de retorno T na superclasse abstrata, criar essa função para criar um novo objeto T e chamar um objeto protegidoString GetRetrieveByKeyQuery()
(definido como abstrato na superclasse) para obter a consulta real a ser executada. Embora isso possa estar ficando um poucoEu sei que é uma pergunta antiga, mas é interessante. O exemplo não é o melhor. Eu acho que seria muito mais claro se você mostrasse um caso de uso:
Simplesmente poder ter métodos estáticos implementando uma interface não alcançaria o que você deseja; o que seria necessário seria ter membros estáticos como parte de uma interface. Certamente posso imaginar muitos casos de uso para isso, especialmente quando se trata de poder criar coisas. Duas abordagens que eu poderia oferecer que podem ser úteis:
Nenhuma dessas abordagens é realmente atraente. Por outro lado, eu esperaria que, se os mecanismos existentes no CLR proporcionassem esse tipo de funcionalidade de maneira limpa, o .net permitiria especificar restrições "novas" parametrizadas (já que saber se uma classe tem um construtor com uma assinatura específica parece ser comparável em dificuldade de saber se ele possui um método estático com uma assinatura específica).
fonte
Míope, eu acho.
Quando originalmente projetadas, as interfaces foram projetadas apenas para serem usadas com instâncias de classe
Foi somente com a introdução de interfaces, já que as restrições para genéricos adicionaram um método estático a uma interface e tiveram um uso prático.
(respondendo ao comentário :) Acredito que alterá-lo agora exigiria uma alteração no CLR, o que levaria a incompatibilidades com os assemblies existentes.
fonte
As interfaces especificam o comportamento de um objeto.
Os métodos estáticos não especificam o comportamento de um objeto, mas o comportamento que afeta um objeto de alguma maneira.
fonte
Na medida em que as interfaces representam "contratos", parece razoável que as classes estáticas implementem interfaces.
Todos os argumentos acima parecem não entender esse ponto sobre contratos.
fonte
Como o objetivo de uma interface é permitir o polimorfismo, ser capaz de passar uma instância de qualquer número de classes definidas que foram definidas para implementar a interface definida ... garantindo que, dentro da sua chamada polimórfica, o código possa encontrar o método que você está chamando. não faz sentido permitir que um método estático implemente a interface,
Como você chamaria isso?
fonte
Whatever<T>() where T:IMyStaticInterface
eu poderia chamarWhatever<MyClass>()
e terT.MyStaticMethod()
dentro daWhatever<T>()
implementação sem precisar de uma instância. O método a ser chamado seria determinado em tempo de execução. Você pode fazer isso através da reflexão, mas não há "contrato" a ser forçado.Em relação aos métodos estáticos usados em contextos não genéricos, concordo que não faz muito sentido permiti-los nas interfaces, pois você não seria capaz de chamá-los se tivesse uma referência à interface. No entanto, existe um furo fundamental no design da linguagem criado usando interfaces NÃO em um contexto polimórfico, mas em um genérico. Nesse caso, a interface não é uma interface, mas uma restrição. Como o C # não tem conceito de restrição fora de uma interface, falta uma funcionalidade substancial. Caso em questão:
Aqui não há polimorfismo, o genérico usa o tipo real do objeto e chama o operador + =, mas isso falha, pois não pode ter certeza de que esse operador existe. A solução simples é especificá-lo na restrição; a solução simples é impossível porque os operadores são estáticos e os métodos estáticos não podem estar em uma interface e (aqui está o problema) as restrições são representadas como interfaces.
O que o C # precisa é de um tipo de restrição real, todas as interfaces também seriam restrições, mas nem todas as restrições seriam interfaces, então você pode fazer isso:
Já se falou muito em fazer uma IAritmética para todos os tipos numéricos, mas existe uma preocupação com a eficiência, uma vez que uma restrição não é uma construção polimórfica, fazendo uma restrição CAritmética resolveria esse problema.
fonte
T
deve ter um estático membro (por exemplo, uma fábrica que produzT
instâncias). Mesmo sem mudanças no tempo de execução, acho que seria possível definir convenções e métodos auxiliares que permitiriam que as linguagens implementassem algo como açúcar sintático de maneira eficiente e interoperável. Se para cada interface com métodos estáticos virtuais houve uma classe auxiliar ...Como as interfaces estão na estrutura de herança, e os métodos estáticos não herdam bem.
fonte
O que você parece querer permitirá que um método estático seja chamado por meio do Type ou de qualquer instância desse tipo. Isso resultaria no mínimo em ambiguidade que não é uma característica desejável.
Haveria debates intermináveis sobre se isso importava, qual é a melhor prática e se há problemas de desempenho de uma maneira ou de outra. Simplesmente não suportá-lo, o C # evita a preocupação com isso.
Também é provável que um compilador que esteja em conformidade com esse desejo perca algumas otimizações que podem vir com uma separação mais estrita entre instância e métodos estáticos.
fonte
Você pode pensar nos métodos estáticos e não estáticos de uma classe como sendo interfaces diferentes. Quando chamados, os métodos estáticos são resolvidos para o objeto de classe estática singleton e os métodos não estáticos são resolvidos para a instância da classe com a qual você lida. Portanto, se você usar métodos estáticos e não estáticos em uma interface, estará efetivamente declarando duas interfaces quando realmente queremos que as interfaces sejam usadas para acessar uma coisa coesa.
fonte
Para dar um exemplo em que estou perdendo a implementação estática dos métodos de interface ou o que Mark Brackett introduziu como o "método do tipo chamado":
Ao ler de um armazenamento de banco de dados, temos uma classe DataTable genérica que manipula a leitura de uma tabela de qualquer estrutura. Todas as informações específicas da tabela são colocadas em uma classe por tabela que também contém dados para uma linha do banco de dados e que deve implementar uma interface IDataRow. Incluído no IDataRow está uma descrição da estrutura da tabela para ler no banco de dados. O DataTable deve solicitar a estrutura de dados do IDataRow antes de ler o banco de dados. Atualmente, isso se parece com:
O GetDataStructure é necessário apenas uma vez para cada tabela ler, a sobrecarga para instanciar mais uma instância é mínima. No entanto, seria bom neste caso aqui.
fonte
FYI: Você pode obter um comportamento semelhante ao que deseja criando métodos de extensão para a interface. O método de extensão seria um comportamento estático compartilhado e não substituível. Infelizmente, porém, esse método estático não faria parte do contrato.
fonte
Interfaces são conjuntos abstratos de funcionalidade disponível definida.
Se um método nessa interface se comporta ou não como estático é um detalhe de implementação que deve estar oculto atrás da interface . Seria errado definir um método de interface como estático, porque você estaria forçando desnecessariamente o método a ser implementado de uma certa maneira.
Se os métodos fossem definidos como estáticos, a classe que implementa a interface não seria tão encapsulada quanto poderia ser. O encapsulamento é uma coisa boa a se esforçar no design orientado a objetos (não vou explicar o porquê, você pode ler isso aqui: http://en.wikipedia.org/wiki/Object-oriented ). Por esse motivo, métodos estáticos não são permitidos em interfaces.
fonte
As classes estáticas devem poder fazer isso para que possam ser usadas genericamente. Eu tive que implementar um Singleton para alcançar os resultados desejados.
Eu tive várias classes de estática da camada de negócios que implementaram métodos CRUD como "Criar", "Ler", "Atualizar", "Excluir" para cada tipo de entidade como "Usuário", "Equipe", etc. controle que possuía uma propriedade abstrata para a classe Business Layer que implementava os métodos CRUD. Isso me permitiu automatizar as operações "Criar", "Ler", "Atualizar", "Excluir" da classe base. Eu tive que usar um Singleton por causa da limitação estática.
fonte
A maioria das pessoas parece esquecer que nas Classes OOP também são objetos e, portanto, possuem mensagens, que por algum motivo o c # chama de "método estático". O fato de existirem diferenças entre objetos de instância e objetos de classe mostra apenas falhas ou deficiências no idioma. Otimista sobre c # embora ...
fonte
OK, aqui está um exemplo de necessidade de um 'método de tipo'. Estou criando um de um conjunto de classes com base em algum XML de origem. Então eu tenho um
função que é chamada por sua vez em cada classe.
A função deve ser estática, caso contrário, perdemos tempo criando objetos inadequados. Como o @Ian Boyde aponta, isso poderia ser feito em uma classe de fábrica, mas isso apenas adiciona complexidade.
Seria bom adicioná-lo à interface para forçar os implementadores de classe a implementá-lo. Isso não causaria uma sobrecarga significativa - é apenas uma verificação do tempo de compilação / link e não afeta a vtable.
No entanto, também seria uma melhoria bastante pequena. Como o método é estático, eu, como responsável pela chamada, devo chamá-lo explicitamente e, portanto, obter um erro de compilação imediato se não for implementado. Permitir que ele seja especificado na interface significaria que esse erro ocorre marginalmente no início do ciclo de desenvolvimento, mas isso é trivial comparado a outros problemas de interface quebrada.
Portanto, é um recurso potencial menor que, em geral, é melhor deixar de fora.
fonte
O fato de uma classe estática ser implementada em C # pela Microsoft, criando uma instância especial de uma classe com os elementos estáticos é apenas uma esquisitice de como a funcionalidade estática é alcançada. Não é um ponto teórico.
Uma interface deve ser um descritor da interface da classe - ou como ela é interagida, e que deve incluir interações que são estáticas. A definição geral de interface (de Meriam-Webster): o local ou área em que coisas diferentes se encontram e se comunicam ou se afetam. Quando você omite componentes estáticos de uma classe ou classes estáticas completamente, estamos ignorando grandes seções de como esses meninos maus interagem.
Aqui está um exemplo muito claro de onde ser capaz de usar interfaces com classes estáticas seria bastante útil:
Atualmente, escrevo as classes estáticas que contêm esses métodos sem nenhum tipo de verificação para garantir que não esqueci nada. É como nos velhos tempos de programação anteriores à OOP.
fonte
O C # e o CLR devem oferecer suporte a métodos estáticos nas interfaces, como o Java. O modificador estático faz parte de uma definição de contrato e tem significado, especificamente que o comportamento e o valor de retorno não variam com base na instância, embora ainda possa variar de uma chamada para outra.
Dito isso, eu recomendo que quando você quiser usar um método estático em uma interface e não puder, use uma anotação. Você obterá a funcionalidade que está procurando.
fonte
Eu acho que a resposta curta é "porque é de zero utilidade". Para chamar um método de interface, você precisa de uma instância do tipo Nos métodos de instância, você pode chamar qualquer método estático que desejar.
fonte
Penso que a questão está no fato de que o C # precisa de outra palavra-chave, exatamente para esse tipo de situação. Você deseja um método cujo valor de retorno dependa apenas do tipo no qual é chamado. Você não pode chamá-lo de "estático" se esse tipo for desconhecido. Mas uma vez que o tipo se torne conhecido, ele se tornará estático. "Estática não resolvida" é a idéia - ainda não é estática, mas assim que soubermos o tipo de recebimento, será. Esse é um conceito perfeitamente bom, e é por isso que os programadores continuam pedindo por ele. Mas não se encaixava na maneira como os designers pensavam sobre a linguagem.
Como não está disponível, passei a usar métodos não estáticos da maneira mostrada abaixo. Não é exatamente o ideal, mas não vejo nenhuma abordagem que faça mais sentido, pelo menos não para mim.
fonte
Portanto, se você deseja acessar os métodos de contrato de interface, é necessário criar um objeto. É sempre necessário que não seja permitido no caso de métodos estáticos. Classes, métodos e variáveis estáticas nunca exigem objetos e são carregados na memória sem criar objetos nessa área (ou classe) ou você pode dizer que não exige Criação de Objetos.
fonte
Conceitualmente não há razão para que uma interface não possa definir um contrato que inclua métodos estáticos.
Para a implementação atual da linguagem C #, a restrição se deve à permissão de herança de uma classe base e interfaces. Se "class SomeBaseClass" implementa "interface ISomeInterface" e "class SomeDerivedClass: SomeBaseClass, ISomeInterface" também implementa a interface, um método estático para implementar um método de interface falharia na compilação porque um método estático não pode ter a mesma assinatura que um método de instância (o que estar presente na classe base para implementar a interface).
Uma classe estática é funcionalmente idêntica a um singleton e serve ao mesmo propósito que um singleton com sintaxe mais limpa. Como um singleton pode implementar uma interface, as implementações de interface por estática são conceitualmente válidas.
Por isso, tudo se resume à limitação do conflito de nomes em C #, por exemplo, e métodos estáticos com o mesmo nome na herança. Não há razão para que o C # não possa ser "atualizado" para oferecer suporte a contratos de método estático (interfaces).
fonte
Quando uma classe implementa uma interface, está criando uma instância para os membros da interface. Embora um tipo estático não tenha uma instância, não há sentido em ter assinaturas estáticas em uma interface.
fonte