Como posso implementar métodos estáticos em uma interface?


96

Eu tenho uma DLL C ++ de terceiros que chamo de C #.

Os métodos são estáticos.

Quero abstrair isso para fazer alguns testes de unidade, então criei uma interface com os métodos estáticos, mas agora meus erros de programa com:

O modificador 'estático' não é válido para este item

MyMethod cannot be accessed with an instance reference; qualify it with a type name instead

Como posso conseguir essa abstração?

Meu código se parece com este

private IInterfaceWithStaticMethods MyInterface;

public MyClass(IInterfaceWithStaticMethods myInterface)
{
  this.MyInterface = myInterface;
}

public void MyMethod()
{
  MyInterface.StaticMethod();
}

3
Talvez você possa fazer isso com métodos de extensão: stackoverflow.com/questions/1243921/…
hcb

Respostas:


52

Você não pode definir membros estáticos em uma interface em C #. Uma interface é um contrato para instâncias .

Eu recomendaria criar a interface como você está atualmente, mas sem a palavra-chave estática. Em seguida, crie uma classe StaticIInterfaceque implemente a interface e chame os métodos C ++ estáticos. Para fazer o teste de unidade, crie outra classe FakeIInterface, que também implemente a interface, mas faça o que você precisa para lidar com seus testes de unidade.

Depois de definir essas 2 classes, você pode criar a que precisa para o seu ambiente e passá-la para MyClasso construtor de.


67
-1 para dizer An interface is a contract, not an implementation.- isso é verdade, mas completamente irrelevante ( non sequitur ) aqui, uma vez que o método estático não faz parte da implementação em si - a implementação, por definição, é baseada em dados , que são, por sua vez, inacessíveis para membros estáticos. An interface type definition can define and implement static methods (see §8.4.3) since static methods are associated with the interface type itself rather than with any value of the type.- tenha em mente que os staticmembros são geralmente métodos utilitários .

3
Eu entendo e concordo com suas declarações e sinto que seu comentário é um contexto importante também. Apesar. ao projetar uma interface, deve-se pensar nisso como um contrato, o que implica que métodos estáticos não se aplicam. Achei que deveria deixá-lo lá para ajudar algumas pessoas a entender o propósito de uma interface. A comunidade acha que deve ser removido?
Davisoa

1
Concordo parcialmente que An interface is a contract, not an implementationé inútil, às vezes um pouco de contextualização ajuda muito. E eu concordo totalmente com static method is not a part of implementation itself , métodos estáticos têm uma implementação, eles se tornam parte da implementação somente se usados ​​como implementação na implementação de outro método. Porém meu dicionário é baseado no que aprendi, pelo que eu sei, a terminologia realmente varia dependendo também da linguagem de programação. Os métodos estáticos não podem ser interfaces porque, de qualquer forma, pode haver apenas 1 implementação.
CoffeDeveloper

Imagine que tenho um IPersoncontrato que estabelece que GetCountryfornecerá o nome do país de origem da pessoa ... as FrenchPersonentidades dirão "França" e GermanPersontodas dirão "Alemanha", também útil quando diferentes tipos de entidades compartilham a mesma Tabela (Dados), como MS Azure um, dizem Connection, Poste Commentsão armazenados na UsersAzureTable, então entidades de árvores têm uma informação compartilhada, IUserspoderia ter GetTableNamemétodo estático ...
Serge

@vaxquis - IMHO, "é um contrato" seria relevante se a frase fosse reformulada: `Uma interface é um contrato para instâncias . Os membros estáticos fazem parte do tipo; esta frase reformulada diz (corretamente) que eles não têm significado em um contrato de instância. Portanto, acho que o problema é apenas uma redação imprecisa, não um non sequitur.
ToolmakerSteve

113

As interfaces não podem ter membros estáticos e os métodos estáticos não podem ser usados ​​como implementação de métodos de interface.

O que você pode fazer é usar uma implementação de interface explícita:

public interface IMyInterface
{
    void MyMethod();
}

public class MyClass : IMyInterface
{
    static void MyMethod()
    {
    }

    void IMyInterface.MyMethod()
    {
        MyClass.MyMethod();
    }
}

Como alternativa, você pode simplesmente usar métodos não estáticos, mesmo que eles não acessem nenhum membro específico da instância.


18
Para quem está se perguntando por que alguém iria querer fazer isso, é particularmente útil ao escrever testes de unidade / integração para código legado que implementa métodos estáticos.
Dezzamondo

Essa técnica funcionou muito bem para implementar uma API RESTful rápida que precisava manter os dados, mas não podia usar um banco de dados. A implementação estava trabalhando apenas com objetos C # na memória, então não havia lugar para armazenar dados, mas o uso de uma propriedade estática aliviou a necessidade de um banco de dados na memória usando EF Core ou SQLite.
gware

22

Você pode definir métodos estáticos em c # 8, mas deve declarar um corpo padrão para ele.

    public interface IMyInterface
    {
          static string GetHello() =>  "Default Hello from interface" ;
          static void WriteWorld() => Console.WriteLine("Writing World from interface");
    }

ou se você não quiser ter nenhum corpo padrão, basta lançar uma exceção:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }

Parece que membros estáticos em interfaces são bastante inúteis porque você não pode acessá-los por instância de interface. Pelo menos em C # 8.
Pavel Sapehin

4
como um ponto de vista de implementação da Interface, seu direito. é inútil. mas dessa forma, pelo menos, você tem certeza de ter um método implementado em cada classe que está usando essa interface. (este é um tipo de implementação opcional para interfaces)
AliReza

19

Membros estáticos são perfeitamente legais no CLR, mas não no C #.

Você poderia implementar alguma cola em IL para vincular os detalhes de implementação.

Não tem certeza se o compilador C # permitiria chamá-los?

Consulte: 8.9.4 Definição do tipo de interface ECMA-335.

Os tipos de interface são necessariamente incompletos, uma vez que não dizem nada sobre a representação dos valores do tipo de interface. Por esta razão, uma definição de tipo de interface não deve fornecer definições de campo para valores do tipo de interface (ou seja, campos de instância), embora possa declarar campos estáticos (ver §8.4.3).

Da mesma forma, uma definição de tipo de interface não deve fornecer implementações para quaisquer métodos nos valores de seu tipo. No entanto, uma definição de tipo de interface pode - e geralmente o faz - definir contratos de método (nome do método e assinatura do método) que devem ser implementados por tipos de suporte. Uma definição de tipo de interface pode definir e implementar métodos estáticos (ver §8.4.3), uma vez que os métodos estáticos são associados ao próprio tipo de interface em vez de a qualquer valor do tipo.


10
Para referência, CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.ele continua dizendo que não há problema em consumidores compatíveis com CLS rejeitarem esses tipos de interfaces. Tentei há cerca de um ano chamar um método estático em uma interface e o compilador C # não o compilou.
Christopher Currens

Além da observação de @ChristopherCurrens sobre o CLS: Common Language Specification (CLS) is a set of basic language features that .Net Languages needed.... When there is a situation to communicate Objects written in different .Net Complaint languages , those objects must expose the features that are common to all the languages. Faz sentido que se o CLS é sobre interoperabilidade entre diferentes linguagens .NET e o C # não permite membros estáticos em uma interface, então o CLS os proibiria também, para garantir bibliotecas em outras linguagens .NET podem ser chamadas de C #.
Simon Tewsi

5

Você pode invocá-lo com reflexão:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);

5
E se você não tiver uma instância de MyInterface, você pode usar "typeOf (MyInterface)" em vez de "myInterface.GetType ()".
RenniePet

Pareceu uma boa ideia na época, e posso continuar a fazê-lo por meio de reflexão, mas um pequeno aviso: torna-se mais problemático se o programa for ofuscado de forma que o método StaticMethod seja renomeado.
RenniePet

1
@RenniePet: Você poderia lidar parcialmente com a renomeação de StaticMethod usando nameof (StaticMethod). PODE ajudar com um ofuscador, dependendo de como ele renomeia. Se você fizer dessa maneira, pelo menos verá um erro de tempo de compilação.
Brent Rittenhouse

A reflexão é muito extrema para este caso
Stepan Ivanenko

3

OC # "Ten" vai permitir membros estáticos nas interfaces , junto com as funções. É um grande avanço, pois permitirá sobrecarregar operadores genéricos também, sem qualquer uso de reflexão. Aqui está um trecho de exemplo de como isso funciona, usando o exemplo clássico de monóide, que é apenas um jargão para dizer "algo que pode ser adicionado". Retirado diretamente de Mads Torgersen: C # into the Future :

interface IMonoid<T>
{
    static T Zero { get; }
    static T operator +(T t1, T t2);
}

public static T AddAll<T>(T[] ts) where T : IMonoid<T>
{
    T result = T.Zero;
    foreach (T t in ts) { result += t; }
    return result;
}

role IntAddMonoid extends int : IMonoid<int>
{
    public static int Zero => 0;
}

IntAddMonoid[] values = new int[] {1, 2, 4, 8, 16, 32};
int sixtyThree = AddAll<IntAddMonoid>(values); // == 63

Recursos adicionais:

Jeremy Bytes: membros estáticos da interface C # 8

EDITAR

Este post originalmente afirmava que os membros estáticos da interface seriam adicionados em C # 8.0 , o que não é verdade, interpretei mal as palavras de Mads Torgersen no vídeo. O guia oficial do C # 8.0 ainda não fala sobre membros da interface estática, mas está claro que eles estão trabalhando nisso há muito tempo.


1

Por que você não pode ter um método estático em uma interface: Por que o C # não permite que métodos estáticos implementem uma interface?

No entanto, eu sugeriria remover os métodos estáticos em favor dos métodos de instância. Se isso não for possível, você pode envolver as chamadas de método estático dentro de um método de instância e, em seguida, criar uma interface para isso e executar seus testes de unidade a partir dela.

ie

public static class MyStaticClass
{
    public static void MyStaticMethod()
    {...}
}

public interface IStaticWrapper
{
    void MyMethod();
}

public class MyClass : IStaticWrapper
{
    public void MyMethod()
    {
        MyStaticClass.MyStaticMethod();
    }
}

qual é a vantagem de usar interface com classe estática em vez de usar apenas interface?
Selen

1

C # 8 permite membros estáticos nas interfaces

A partir do C # 8.0, uma interface pode definir uma implementação padrão para membros. Ele também pode definir membros estáticos para fornecer uma única implementação para funcionalidade comum.

interface (referência C #)

Por exemplo

public interface IGetSomething
{
    public static string Something = "something";
}

var something = IGetSomething.Something;

0

Não vejo problemas além do compilador pensar que eu não deveria fazer isso. C # não pode herdar de mais de uma classe base, que chatice quando você está acostumado a fazer isso, frontalmente você pode fazer várias interfaces, então eu abusei disso para obter os recursos de que preciso ;-)

Você deve verificar se há null, etc., no entanto, aqui está uma versão simplificada que implementa o Parse para obter uma classe de um serviço da web ou banco de dados

/// <summary>
/// Implements parse
/// </summary>
/// <typeparam name="T">the type to parse</typeparam>
public interface IParse<T>
{ 
    /// <summary>
    /// implements parse from string to type
    /// </summary>
    /// <param name="text">value to parse</param>
    /// <returns></returns>
    static T Parse(string text)=>JsonConvert.DeserializeObject<T>(text, settings:new JsonSerializerSettings() { ConstructorHandling= ConstructorHandling.AllowNonPublicDefaultConstructor });

    /// <summary>
    /// implements parse from string to type
    /// </summary>
    /// <param name="text">value to parse</param>
    /// <param name="settings">the settings to us</param>
    /// <returns></returns>
    static T Parse(string text, JsonSerializerSettings settings) =>JsonConvert.DeserializeObject<T>(text, settings);
}

Então, apenas chamo a interface no código que possui List como o valor de retorno.

Aqui está um recorte em que li JSON do banco de dados, preenchendo-o para um tipo que tem Json implementado

//some plugging code

using (var reader = cmd.ExecuteReader(behavior: CommandBehavior.CloseConnection | CommandBehavior.SingleResult))
{
    if (reader.HasRows)
    {
        while (reader.Read())
        {
            rows++;
            try
            {
                var json = reader.GetString(0);

                result.Add(IParse<T>.Parse(json));
            }
            catch
            {
                failed++;
            }
        }
    }
}
//other plugging code

Com> ver versão 8 você tem a implementação padrão, então "a caixa de Pandora está aberta"

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.