Por que o C # não permite que métodos estáticos implementem uma interface?

447

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;
    }

    ....

 }
Kramii
fonte
6
Bem, o Java 8 possui ( stackoverflow.com/questions/23148471/… ).
liang
1
Veja como você pode combinar um comportamento estático com herança ou implementação de interface: stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes
1
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)
Jeroen Mostert
2
Apenas deixando todos saberem que a espera acabou agora! C # 8.0 tem métodos de interface estáticos: dotnetfiddle.net/Lrzy6y (embora eles funcionam um pouco diferente de como OP queria que eles trabalho - você não tem que implementá-las)
Jamie Twells

Respostas:

221

Supondo que você esteja se perguntando por que não pode fazer isso:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

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.

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

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.

Chris Marasti-Georg
fonte
6
Um exemplo perfeito! Mas não sei se entendi seu raciocínio. Certamente o compilador poderia ter sido projetado para olhar também para os membros do statuc? As instâncias não possuem uma tabela de endereços para a implementação desses métodos? Os métodos estáticos não poderiam ser incluídos nesta tabela?
Kramii 3/11
12
Há um caso em que isso pode ser útil. Por exemplo, quero que todos os implementadores implementem um método GetInstance que aceite um argumento XElement. Não posso definir isso como um método estático na interface, nem exigir uma assinatura de construtor da interface.
Oleks
25
Muitas pessoas pesaram nos dois lados: de "Isso não faz sentido" a "É um bug, eu gostaria que você pudesse". (Acho que existem casos de uso válidos para isso, e foi assim que acabei aqui.) Como solução alternativa, o método de instância pode simplesmente delegar para um método estático.
harpo 31/03
5
Você também pode simplesmente implementar a peça como um método de extensão na interface base, como em: 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.
Troy Alford
8
Isso faria muito sentido com os genéricos. Por exemplo, void Something <T> () onde T: ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky I
173

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 ):

... o princípio básico de design dos métodos estáticos, o princípio que lhes dá nome ... [é] ... sempre pode ser determinado exatamente, em tempo de compilação, como o método será chamado. Ou seja, o método pode ser resolvido apenas pela análise estática do código.

Observe que Lippert deixa espaço para o chamado método de tipo:

Ou seja, um método associado a um tipo (como estático), que não utiliza um argumento "this" não anulável (diferente de uma instância ou virtual), mas um método em que o método chamado dependeria do tipo construído de T ( diferente de uma estática, que deve ser determinável em tempo de compilação).

mas ainda está para ser convencido de sua utilidade.

Mark Brackett
fonte
5
Excelente, esta é a resposta que eu queria escrever - simplesmente não sabia os detalhes da implementação.
Chris Marasti-Georg
5
Ótima resposta. E eu quero esse 'método de tipo'! Seria útil em tantas ocasiões (pense em metadados para um tipo / classe).
Philip Daubmeier
23
Essa é a resposta correta. Você passa uma interface para alguém, eles precisam saber como chamar um método. Uma interface é apenas uma tabela de método virtual . Sua classe estática não tem isso. O chamador não saberia como chamar um método. (Antes de ler esta resposta eu pensei C # só estava sendo pedante. Agora eu percebo que é uma limitação técnica, imposta por que uma interface é ). Outras pessoas falarão com você sobre como é um design ruim. Não é um design ruim - é uma limitação técnica.
11262 Ian Boyd
2
+1 por responder à pergunta e não ser pedante e arrogante. Como Ian Boyd disse: "Não é um projeto ruim - é uma limitação técnica".
30513 Joshua Pech
3
É totalmente possível gerar um objeto para uma classe estática com a vtable associada. Veja como o Scala lida com se objecte como eles têm permissão para implementar interfaces.
Sebastian Graf
97

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:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

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:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

para usar a instanciação para resolver o problema da interface estática, precisamos fazer o seguinte:

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

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!

Ivan Arjentinski
fonte
35
+1 em "A maioria das respostas aqui parece não entender o ponto."; é incrível como parece que praticamente todas as respostas esquivar-se do cerne da questão se aprofundar palavreado em sua maioria inúteis ...
5
@ Chris Aqui está um exemplo específico que me fez acertar essa restrição novamente. Desejo adicionar uma interface IResettable às classes para indicar que elas armazenam em cache determinados dados em variáveis ​​estáticas que podem ser redefinidas pelo administrador do site (por exemplo, uma lista de categorias de pedidos, um conjunto de taxas de IVA, uma lista de categorias recuperadas de uma API externa) para reduzir o banco de dados e as ocorrências externas da API, isso obviamente usaria uma redefinição estática do método. Isso me permite automatizar a detecção de quais classes podem ser redefinidas. Ainda posso fazer isso, mas o método não é imposto ou adicionado automaticamente ao IDE e depende da esperança.
mattmanser
6
@ Chris Eu discordo, um enorme exagero. É sempre um sinal de falha de linguagem quando mais arquitetura é a melhor solução. Lembre-se de todos os padrões sobre os quais ninguém mais fala desde que o C # obteve genéricos e métodos anônimos?
mattmanser
3
Você não pode usar "where T: IQueryable, T: IDoSomeStaticMath" ou semelhante?
Roger Willcocks
2
@ ChrisMarasti-Georg: Uma maneira um tanto suja, mas interessante, de contornar isso é essa pequena construção:, public abstract class DBObject<T> where T : DBObject<T>, new()e depois fazer todas as classes de banco de dados herdarem como DBObject<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 protegido String GetRetrieveByKeyQuery()(definido como abstrato na superclasse) para obter a consulta real a ser executada. Embora isso possa estar ficando um pouco
problemático
20

Eu 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:

string DoSomething <T> () em que T: ISomeFunction
{
  if (T.someFunction ())
    ...
}

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:

  1. Crie uma classe genérica estática cujo parâmetro de tipo será o tipo que você passaria para o DoSomething acima. Cada variação dessa classe terá um ou mais membros estáticos mantendo itens relacionados a esse tipo. Essas informações podem ser fornecidas fazendo com que cada classe de interesse chame uma rotina "registrar informações" ou usando o Reflection para obter as informações quando o construtor estático da variação de classe é executado. Acredito que a última abordagem seja usada por coisas como Comparer <T> .Default ().
  2. Para cada classe T de interesse, defina uma classe ou estrutura que implemente IGetWhateverClassInfo <T> e atenda a uma "nova" restrição. A classe não conterá nenhum campo, mas terá uma propriedade estática que retorna um campo estático com as informações do tipo. Passe o tipo dessa classe ou estrutura para a rotina genérica em questão, que poderá criar uma instância e usá-la para obter informações sobre a outra classe. Se você usar uma classe para esse fim, provavelmente deverá definir uma classe genérica estática conforme indicado acima, para evitar a necessidade de construir uma nova instância de objeto descritor a cada vez. Se você usar uma estrutura, o custo da instanciação deve ser nulo, mas cada tipo de estrutura diferente exigiria uma expansão diferente da rotina DoSomething.

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).

supercat
fonte
16

Míope, eu acho.

Quando originalmente projetadas, as interfaces foram projetadas apenas para serem usadas com instâncias de classe

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

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.

James Curran
fonte
Foi no contexto dos genéricos que encontrei o problema pela primeira vez, mas me pergunto se a inclusão de métodos estáticos nas interfaces também poderia ser útil em outros contextos? Existe uma razão pela qual as coisas não podem ser mudadas?
Kramii
Eu também encontro isso ao implementar uma classe genérica que requer que o tipo de parâmetro se crie com alguns parâmetros. Como o new () não pode receber nenhum. Você já descobriu como fazer isso, Kramii?
Tom
1
@ Kramii: Contratos para APIs estáticas. Eu não quero uma instância de objeto, apenas uma garantia de uma assinatura específica, por exemplo. IMatrixMultiplier ou ICustomSerializer. Funções / ações / delegados como membros da classe fazem o truque, mas na IMO isso às vezes parece um exagero e pode ser confuso para os inexperientes que tentam estender a API.
David Cuccia
14

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.

John Kraft
fonte
71
Desculpe .. não tenho certeza se está certo! Uma interface não especifica comportamento. Uma interface define um conjunto de operações nomeadas. Duas classes poderiam implementar o método de uma interface para se comportar de maneiras completamente diferentes. Portanto, uma interface não especifica nenhum comportamento. A classe que implementa isso.
Scott Langham
1
Espero que você não pense que estou sendo exigente ... mas acho que é uma distinção importante para quem aprende OO a entender.
Scott Langham
4
Uma interface deve especificar um contrato que inclua comportamento e apresentação. É por isso que alterar o comportamento de uma chamada de interface é um não-não, pois ambos devem ser corrigidos. Se você tiver uma interface em que a chamada atue de maneira diferente (por exemplo, IList.Add removeu), não seria correto.
Jeff Yates
14
Bem, sim, você seria deformado na cabeça para definir um método para se comportar incongruentemente com seu nome. Se você tivesse o IAlertService.GetAssistance (), seu comportamento pode ser acender uma luz, acionar um alarme ou cutucar os olhos com um pau.
Scott Langham
1
Uma implementação também seria capaz de gravar em um arquivo de log. Esse comportamento não está especificado na interface. Mas talvez você esteja certo. O comportamento de 'obter assistência' deve realmente ser respeitado.
23475 Scott Scott
14

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.

George
fonte
2
Eu concordo totalmente com esta resposta simples, mas eficaz. O que seria interessante na "interface estática" é que ela representaria um contrato. Talvez não deva ser chamado de "interface estática", mas ainda perdemos uma construção. Por exemplo, consulte o documento oficial do .NET sobre a interface ICustomMarshaler. Ele requer que a classe que o implementa "adicione um método estático chamado GetInstance que aceite uma String como parâmetro e tenha um tipo de retorno de ICustomMarshaler". Isso seriamente se parece com uma definição de "interface estática" em inglês comum, enquanto eu preferiria isso em c # ...
Simon Mourier
@SimonMourier Essa documentação poderia ter sido escrita com mais clareza, mas você está interpretando mal. Não é a interface ICustomMarshaler que requer o método estático GetInstance. É o atributo de código [MarshalAs] que exige isso. Eles estão usando o padrão de fábrica para permitir que o atributo obtenha uma instância do empacotador anexado. Infelizmente, eles esqueceram completamente de incluir a documentação do requisito GetInstance na página de documentação do MarshalAs (apenas mostra exemplos usando implementações de empacotamento internas).
Scott Gartner
@ ScottGartner - Não entenda seu ponto. O msdn.microsoft.com/en-us/library/… afirma claramente: "Além de implementar a interface ICustomMarshaler, os empacotadores personalizados devem implementar um método estático chamado GetInstance que aceita uma String como parâmetro e tem um tipo de retorno de ICustomMarshaler. Isso O método static é chamado pela camada de interoperabilidade COM do common language runtime para instanciar uma instância do empacotador personalizado. ". Esta é definitivamente uma definição de contrato estática.
Simon Mourier
9

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?


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  
Charles Bretana
fonte
1
Outras linguagens (Java, por exemplo) permitem que métodos estáticos sejam chamados a partir de instâncias de objetos, embora você receba um aviso de que deveria chamá-los de um contexto estático.
Chris Marasti-Georg
Permitir que um método estático seja chamado de uma instância também é permitido no .Net. Isso é uma coisa diferente. Se um método estático implementasse uma interface, você poderia chamá-lo SEM uma instância. É isso que não faz sentido. Lembre-se de que você não pode colocar a implementação em uma interface, ela deve estar na classe. Portanto, se cinco classes diferentes fossem definidas para implementar essa interface e cada uma tivesse uma implementação diferente desse método estático, qual o compilador usaria?
Charles Bretana
1
@CharlesBretana no caso dos genéricos, aquele do tipo passado. Vejo muita utilidade em ter interfaces para métodos estáticos (ou, se você quiser, vamos chamá-los de "interfaces estáticas" e permitir que uma classe defina ambas as interfaces estáticas e interfaces de instância). Então, se eu tiver, Whatever<T>() where T:IMyStaticInterfaceeu poderia chamar Whatever<MyClass>()e ter T.MyStaticMethod()dentro da Whatever<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.
Jcl
4

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:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

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:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

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.

Jeremy Sorensen
fonte
Os operadores em C # são estáticos, portanto isso ainda não faz sentido.
John Saunders
Esse é o ponto, a restrição verifica se o TYPE (não a instância) possui o operador + (e, por extensão, o operador + =) e permite que o modelo seja gerado, quando a substituição real do modelo ocorrer, o objeto em uso será garantido. de um tipo que possui esse operador (ou outro método estático.) Portanto, você pode dizer: SumElements <int> (0, 5); que instanciaria isso: SumElements (int initVal, int [] valores) {foreach (valores de var v em valores) {initVal + = v; }}, O que, naturalmente, faz todo o sentido
Jeremy Sorensen
1
Lembre-se de que não é um modelo. É genérico, que não é o mesmo que modelos C ++.
John Saunders
@JohnSaunders: genéricos não são modelos, e eu não sei como eles poderiam razoavelmente ser feitos para trabalhar com operadores estaticamente vinculados, mas em contextos genéricos há muitos casos em que pode ser útil especificar que um Tdeve ter um estático membro (por exemplo, uma fábrica que produz Tinstâ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 ...
supercat
1
@ JohnSaunders: O problema não é que tanto o método implementa uma interface, mas que o compilador não pode selecionar um método virtual para chamar sem ter uma instância de objeto na qual basear a seleção. Uma solução é despachar a chamada "interface estática" não usando despacho virtual (que não funcionará), mas usando uma chamada para uma classe estática genérica. O envio genérico baseado em tipo não requer uma instância, mas apenas um tipo.
Supercat 14/12
3

Como as interfaces estão na estrutura de herança, e os métodos estáticos não herdam bem.

Joel Coehoorn
fonte
3

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.

AnthonyWJones
fonte
Curiosamente, é bem possível chamar um método estático pelos dois tipos Type ad Instance no VB.Net (mesmo que o IDE dê um aviso no último caso). Não parece ser um problema. Você pode estar certo sobre as otimizações.
Kramii 3/11
3

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.

Scott Langham
fonte
Esse é um ponto de vista interessante e provavelmente é o que os designers de C # tinham em mente. Pensarei nos membros estáticos de uma maneira diferente a partir de agora.
Kramii 3/11
3

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:

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

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
1

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.

Daniel Auger
fonte
1

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.

Scott Langham
fonte
1
Sim, uma estática na declaração da interface seria boba. Uma classe não deve ser forçada a implementar a interface de uma certa maneira. No entanto, o C # restringe uma classe à implementação da interface usando métodos não estáticos. Isso faz sentido? Por quê?
Kramii 3/11
Você não pode usar a palavra-chave 'estático'. Porém, não há restrição, porque você não precisa da palavra-chave estática para escrever um método que se comporte estaticamente. Apenas escreva um método que não acesse nenhum membro do objeto, e ele se comportará como um método estático. Então, há restrição.
23475 Scott
Verdadeiro Scott, mas não permite que alguém acesse esse método de maneira estática, em outra parte do código. Não que eu possa pensar em uma razão que você gostaria, mas isso parece ser o que o OP está perguntando.
Chris Marasti-Georg
Bem, se você realmente sentiu a necessidade, poderia escrevê-lo como um método estático para acessar em outra parte do código e apenas escrever o método não-estático para chamar o método estático. Eu posso estar errado, mas duvido que esse seja um objetivo do questionador.
Scott Langham
1

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.

Louis Rebolloso
fonte
1

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 ...

Mar Bar
fonte
2
O problema é que esta pergunta não é sobre "in OOP". É sobre "em c #". No C #, não há "mensagens".
John Saunders
1

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

  static public bool IsHandled(XElement xml)

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.

Stephen Westlake
fonte
1

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:

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

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.

Thomas Phaneuf
fonte
Esta é uma pergunta antiga; hoje em dia, tentamos muito evitar perguntas com base em opiniões, porque são difíceis de responder com autoridade. A única boa maneira de responder a isso seria descrever as razões pelas quais a MS teve, ou deveria ter demonstrado, fazê-lo da maneira que fizeram.
18715 Nathan Tuggy
1

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.

Daniel Barbalace
fonte
0

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.

mackenir
fonte
0

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.

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}
William Jockusch
fonte
0

Conforme o conceito orientado a objetos, a interface é implementada por classes e possui contrato para acessar essas funções (ou métodos) implementadas usando o objeto.

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.

Sanjeev Saraswat
fonte
0

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).

Greg McPherran
fonte
-1

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.

Vinay Chanumolu
fonte