1. Introdução
Antigamente, interface era tipo regulamento de colégio interno: só assinatura, nada de campos, nada de implementação, nada de membro estático! Mas o .NET evolui, e linguagem de programação é tipo organismo vivo: pra encarar os desafios novos, ela precisa evoluir.
Com as versões novas do C#, as interfaces aprenderam uns truques novos. Um dos mais legais é o membro estático em interface. Agora, interfaces podem ter métodos estáticos, propriedades e eventos estáticos. E nas versões mais recentes do C# (a partir do C# 11) dá até pra declarar métodos estáticos abstratos, que obrigam quem implementa a interface a criar esses métodos.
Isso é uma baita mudança de paradigma, que muda o jeito de pensar programação genérica e orientada a objetos.
Falando de boa: membro estático de interface é um membro "geral", acessado pelo próprio tipo da interface (ou pelo tipo que implementa), e não por uma instância do objeto.
Até pouco tempo atrás, só classe, struct e enum podiam ter métodos e propriedades estáticas, mas agora interface também pode.
Como fica isso? Exemplo de sintaxe
public interface IMyMath
{
static int Add(int a, int b) => a + b; // Método estático (implementação padrão)
static abstract int Multiply(int a, int b); // Tem que implementar no tipo que herda
}
- static — membro acessível no nível do tipo, não da instância.
- static abstract — o contrato "exige" implementar o método estático no tipo que implementa.
- Em interfaces (igual em classes) agora dá pra declarar métodos, propriedades e eventos estáticos. Também dá pra criar constantes. Mas interface ainda não pode ter campos de instância nem campos estáticos (só constantes).
A função dos membros estáticos em interface é permitir declarar "operações" universais. Por exemplo, se você tem uma coleção de objetos e quer chamar "comparar" neles, sem saber qual tipo implementa a interface, dá pra fazer isso com membros estáticos abstratos.
2. Métodos estáticos com implementação na interface
Pra que serve isso?
Problema clássico: você quer descrever não só métodos "de instância" (tipo, fazer algo com o objeto), mas também "estáticos" (tipo, criar um objeto a partir de uma string ou comparar dois objetos de forma estática). Antes, isso era resolvido com patterns (Factory, Comparer, Helper), mas agora dá pra expressar direto na interface.
Isso ficou ainda mais importante pra Generics e algoritmos que trabalham com tipos genéricos:
- Implementação de operadores universais (tipo soma, comparação).
- Restringir código genérico: "Qualquer tipo que tenha método estático ou operador..."
- Serialização/deserialização: quando precisa criar objeto de string sem saber o tipo na hora de codar.
Métodos estáticos com corpo
Com o C# 8, liberaram métodos estáticos com corpo em interface. Eles são tipo métodos estáticos normais de classe.
public interface IUtility
{
static void PrintHello()
{
Console.WriteLine("Hello from Interface!");
}
}
Dá pra chamar assim: IUtility.PrintHello();
Isso é massa pra funções utilitárias que fazem sentido na interface, mas não em uma implementação específica. Tipo: estatística de todos os objetos do tipo, métodos fábrica (CreateDefault), validações gerais (tipo checar valores válidos).
Detalhe: membros estáticos da interface não são "sobrescritos" nas classes
Se você declarou na interface static void Method() { ... }, a classe que implementa pode declarar um método estático igualzinho — mas não é override! São dois métodos independentes — o nome é igual, mas não é "método estático virtual".
3. Membros estáticos abstratos
A partir do C# 11, dá pra declarar static abstract em interface. Isso quer dizer: "toda classe ou struct que implementar essa interface tem que declarar um membro estático com essa assinatura".
Exemplo:
public interface IParsable<T>
{
static abstract T Parse(string s);
}
Qualquer tipo que implementar essa interface tem que declarar o método estático Parse(string s).
Implementando essa interface numa classe
public class Temperature : IParsable<Temperature>
{
public int Value { get; set; }
// Implementação estática!
public static Temperature Parse(string s)
{
var temp = new Temperature();
temp.Value = int.Parse(s);
return temp;
}
}
Como funciona?
Isso fica ainda mais legal em código genérico (Generics):
public static T ParseFromString<T>(string s) where T : IParsable<T>
{
return T.Parse(s);
}
// Usando:
var temp = ParseFromString<Temperature>("42");
Agora dá pra escrever código realmente universal, que funciona com qualquer tipo que implemente esse "comportamento estático"!
4. Membros estáticos em interface vs. membros estáticos normais de classe
| Característica | Membro estático de classe | Membro estático de interface |
|---|---|---|
| É herdado | Não | Não, mas é implementado pelo contrato da interface |
| Exige implementação | Não | Só se for static abstract |
| Usa em Generics | Não (até C# 11) | Sim (com static abstract) |
| Pode ter implementação padrão | Sim | Sim |
| Sobrescrita | Não | Não, só tem que implementar mesmo |
| Visibilidade na chamada | Pelo nome do tipo | Pelo tipo da interface ou do tipo que implementa |
7. Exemplos do mundo real
Vamos ver como dá pra melhorar nosso app de estudo usando essas novidades.
Imagina que temos a interface "IPrintable":
public interface IPrintable
{
void Print();
static void PrintAll(IEnumerable<IPrintable> items)
{
foreach (var item in items)
{
item.Print();
}
}
}
Agora dá pra chamar assim, de boa:
var documents = new List<IPrintable>
{
new Invoice { Number = "INV-001" },
new Receipt { Number = "RC-007" }
};
IPrintable.PrintAll(documents); // método estático da interface!
Essa arquitetura é ótima pra operações "em grupo" em todas as implementações da interface.
Exemplo mais avançado: soma genérica de tipos numéricos
Imagina essa interface:
public interface IAddable<T>
{
static abstract T Add(T left, T right);
}
Implementação pra inteiros (struct wrapper):
public struct MyInt : IAddable<MyInt>
{
public int Value { get; }
public MyInt(int val) => Value = val;
public static MyInt Add(MyInt left, MyInt right) => new MyInt(left.Value + right.Value);
}
E, finalmente, uma função universal pra somar dois números do tipo T:
public static T Sum<T>(T a, T b) where T : IAddable<T>
{
return T.Add(a, b);
}
// Usando:
var x = new MyInt(5);
var y = new MyInt(6);
var z = Sum(x, y); // z.Value == 11
É pra esse tipo de universalidade que criaram membros estáticos em interface!
8. Erros comuns e detalhes
Viver com feature nova nem sempre é tão fácil quanto parece nos exemplos. Olha só uns pontos que podem confundir quem tá começando:
Métodos estáticos da interface não são "herdados" pela classe que implementa. Se declarar na interface static void Foo(), então MyClass.Foo() e IMyInterface.Foo() — são dois métodos totalmente diferentes.
Static abstract é obrigatório implementar. Se esquecer, o compilador reclama que a classe não implementou tudo da interface.
Generic constraints: pra usar membros estáticos abstratos, tem que restringir pelo interface nos parâmetros genéricos (where T : IMyInterface).
Nem todas as ferramentas suportam essas novidades ainda. Por exemplo, Rider, VS Code ou Roslyn antigos nem sempre mostram direito os static abstract da interface se o .NET não for C# 11+.
Não confunda com extension methods de interface: eles são implementados separados e não funcionam como membros estáticos.
GO TO FULL VERSION