O C# tem equivalente à tipagem estrutural do Scala?
-
26-09-2019 - |
Pergunta
No Scala, posso definir tipos estruturais do seguinte modo:
type Pressable = { def press(): Unit }
Isso significa que posso definir uma função ou método que receba como argumento algo que seja pressionável, assim:
def foo(i: Pressable) { // etc.
O objeto que passo para esta função deve ter definido para ele um método chamado press() que corresponda à assinatura de tipo definida no tipo - não aceita argumentos, retorna Unit (versão de void do Scala).
Posso até usar o tipo estrutural inline:
def foo(i: { def press(): Unit }) { // etc.
Basicamente, ele permite que o programador tenha todos os benefícios da digitação com pato e, ao mesmo tempo, tenha o benefício da verificação de tipo em tempo de compilação.
C# tem algo semelhante?Pesquisei no Google, mas não consigo encontrar nada, mas não estou familiarizado com C# em profundidade.Se não houver, há planos para adicionar isso?
Solução
Não, e nenhum plano que eu conheço. Apenas nomeado (em vez de estrutural) subtyping (por exemplo, interfaces).
(Outros podem querer ver também
http://en.wikipedia.org/wiki/nominative_type_system
http://en.wikipedia.org/wiki/structural_type_system
)
(Algumas pessoas podem apontar alguns casos de canto exóticos, como o foreach
declaração usando digitação estrutural para GetEnumerator
, mas este é o exceção ao invés da regra.)
Outras dicas
Não há uma maneira de definir tipos estruturais que tenha uma função específica. Há uma biblioteca que adiciona suporte de digitação de pato a C# que pode ser encontrado aqui.
Este é o exemplo do projeto de digitação de patos. Observe o A digitação de pato acontece em tempo de execução e pode falhar. Entendo também que essa biblioteca gera proxies para os tipos digitados por patos, que estão muito longe do elegante suporte de tempo de compilação que é desfrutado em Scala. Isso é mais provável que seja bom com esta geração de C#.
public interface ICanAdd
{
int Add(int x, int y);
}
// Note that MyAdder does NOT implement ICanAdd,
// but it does define an Add method like the one in ICanAdd:
public class MyAdder
{
public int Add(int x, int y)
{
return x + y;
}
}
public class Program
{
void Main()
{
MyAdder myAdder = new MyAdder();
// Even though ICanAdd is not implemented by MyAdder,
// we can duck cast it because it implements all the members:
ICanAdd adder = DuckTyping.Cast<ICanAdd>(myAdder);
// Now we can call adder as you would any ICanAdd object.
// Transparently, this call is being forwarded to myAdder.
int sum = adder.Add(2, 2);
}
}
Esta é a maneira C# de alcançar a mesma coisa usando as boas interfaces chatas.
interface IPressable {
void Press();
}
class Foo {
void Bar(IPressable pressable) {
pressable.Press();
}
}
class Thingy : IPressable, IPushable, etc {
public void Press() {
}
}
static class Program {
public static void Main() {
pressable = new Thingy();
new Foo().Bar(pressable);
}
}
Como outros observaram, isso não é verdade Disponível no .NET (pois isso é mais uma questão de tempo de execução do que um idioma). No entanto, o .NET 4.0 suporta coisa semelhante para interfaces de comidas importadas e acredito que isso possa ser usado para implementar a digitação estrutural para .NET. Veja esta postagem do blog:
Ainda não tentei brincar com isso, mas acho que poderia permitir que os autores do compilador escrevessem idiomas com digitação estrutural para .NET. (A idéia é que você (ou um compilador) definisse uma interface nos bastidores, mas funcionaria, porque as interfaces seriam tratadas como equivalentes, graças ao recurso de equivalência com).
Além disso, C# 4.0 suporta o dynamic
Palavra -chave que, eu acho, poderia ser interpretada como uma digitação estrutural (sem verificação de tipo estático). A palavra-chave permite chamar métodos em qualquer objeto sem conhecer (em tempo de compilação) se o objeto terá os métodos necessários. Isso é essencialmente a mesma coisa que o projeto "Dato digitando" mencionado pelo Igor (mas isso, é claro, não é uma digitação estrutural adequada).
O padrão aguardável em C# talvez possa ser interpretado como uma instância limitada e ad hoc de subtipagem estrutural/tipagem existencial.O compilador só irá await
objetos que têm acesso a um GetAwaiter()
método que retorna qualquer INotifyCompletion
objeto com um conjunto específico de métodos e propriedades.Como nem o objeto 'awaitable' nem o objeto 'awaiter' precisam implementar qualquer interface (exceto INotifyCompletion
no caso deste último), await
é semelhante a um método que aceita objetos aguardáveis de tipo estrutural.