Extension Methods em C# e Prototypes em JavaScript: Olha essa similaridade INCRÍVEL!

Bora falar sobre uma técnica poderosa (acho que nem tanto, mas é legal) de programação? Hoje o assunto é Extension Methods no C# e Prototypes no JavaScript.

Extension Methods em C#

No C#, os Extension Methods permitem adicionar métodos a tipos já existentes sem alterar o código original. Olha este exemplo:

var text = "C# Extension methods";
text.log();

public static class InjectString {
    public static void log(this string content) {
        Console.WriteLine(content);
    }
}

Se quiser testar: dotnetfiddle.net

Aqui, log() vira um método de string, sem precisar alterar a classe string. Isso funciona para quase qualquer tipo (bem, dynamic não!)

Prototypes em JavaScript

Já no JavaScript, podemos usar Prototypes para fazer algo parecido. Olha pra tu vê 😁:

String.prototype.log = function() {
    console.log(this.valueOf());
}

var text = "JavaScript Extension methods";
text.log();

Se quiser testar: playcode.io

Agora, qualquer string também tem o método log(). 🔥

E no TypeScript? 🤔

No TypeScript, usar Prototypes é meio merda, porque você vai ter que abrir mão de tipagem, e muitas vezes vai ser preciso lançar um as any. Então, não é tão direto como no C#, mas é possível.

Resumo Final

  • C#: Flexível e seguro — funciona para quase qualquer tipo, exceto dynamic.
  • JavaScript: Prototypes são poderosos, mas em TypeScript, exigem um pouco de "gambiarra" (a de lei né pai 😁).

Esse é um post beeem melhor construído, de um post que fiz no linkedin, mas caso queira, pode acomapnha-lo aqui: Linkedin: Extension Methods e Prototype

Abaixo se quiser (só se quiser!) me apoiar, meu projeto precisa desse alcance pra quando for lançado. Então vou deixar abaixo minhas redes, obrigado! 😁

Caso queira acompanhar o projeto Dicionário do bebê - Implementando API do Stripe em V Caso queira saber sobre o WebHulkTest (Hulk esmaga!!) WebHulkTest Abaixo as minhas redes, se quiser ver eu postando algo mais... E eu não tenho nenhuma experiência com isso.

Linkedin: André Luiz Youtube: @Ddiidev Github: @Ddiidev X: @luizdidev

Extension Method:

  • É syntax sugar para um método estático
  • É algo isolado do objeto
  • Não tem acesso ao objeto todo
  • Só é acessível se ele estiver em um namespace que está importado
  • Não participa de herança
  • É garantido em tempo de compilação

Prototype:

  • É syntax sugar para um método normal, como são todos os métodos de JS
  • Passa fazer parte do objeto
  • Acessa todo o objeto
  • Ele é carregado em toda a vida do objeto
  • Objetos filhos deste também terão o método novo
  • Pode dar erro achando que ele está presente mas não está

Então é similiar, mas completamente diferente.


Farei algo que muitos pedem para aprender a programar corretamente, gratuitamente (não vendo nada, é retribuição na minha aposentadoria) (links aqui no perfil também).

Só um pontinho. Nada a discordar, mas acrescentar. Quando crio uma extension method, eu sempre procuro adicionar ao global.using, então não está errado, porém acho que é de bom tom, quando vocêr quer algo que tenha comportamento nativo do Tipo(acho que entendeu), ser importado globalmente. Quanto a prototype, não tenho muito mais informação a agregar pois conheço pouco, apenas para o que utilizei e por isso também que senti uma similaridade no comportamento que queria ter em js, que tenho em C#. Obrigado, por agregar ao post!
> *senti uma similaridade no comportamento* A similaridade é apenas na forma como vc usa o método, ou seja, chamando `objeto.novoMetodo()`, como se o `novoMetodo` fosse realmente um método de `objeto`. Mas por baixo dos panos, o mecanismo é completamente diferente. Em C#, vc apenas **tem a impressão** que o método foi criado na classe em questão. Mas é só um _syntax sugar_ para a chamada do método estático: no seu exemplo, `text.log()` é automaticamente convertida para `InjectString.log(text)`. Mas a classe `string` permanece inalterada, nenhum método foi realmente adicionado nela. Por outro lado, em JavaScript, vc realmente está alterando a estrutura do objeto. O método passa a fazer parte dele, e inclusive é herdado pelos objetos filhos. E tem que tomar cuidado ao adicionar coisas no protótipo, principalmente se vc usa `for..in` para iterar. Por exemplo, este código: ```javascript Array.prototype.novaFuncao = function() { // faz algo }; var array = ['a', 'b']; for (var i in array) { console.log(`${i} -> ${array[i]}`); } ``` A saída é: ```none 0 -> a 1 -> b novaFuncao -> function() { // faz algo } ``` Isso porque `for..in` também traz as propriedades que estão no protótipo do objeto, mas nem sempre pode ser o que vc quer ao iterar por ele. Então tem que [tomar alguns cuidados](https://pt.stackoverflow.com/q/449732/112052) para evitar o *prototype pollution*. --- # Não é exclusivo dessas linguagens Vale lembrar que isso também existe em outras linguagens. Por exemplo, em Ruby vc pode modificar qualquer classe: ```ruby # Supondo que UmaClasseQualquer já exista, eu posso adicionar novos métodos nela class UmaClasseQualquer def novoMetodo(x) # faz algo end end ``` E neste caso, vc está de fato adicionando um novo método na classe. Inclusive, dá pra mudar até as classes mais básicas do sistema (embora não seja recomendado): ```ruby # Em Ruby, posso fazer isso. # Assim, todas as somas entre números inteiros dará o mesmo resultado class Integer def +(other) 42 end end # todas as expressões abaixo resultam em 42 puts 10 + 20 puts 2 + 2 puts 10000 + 2000000 ``` Em Python também dá pra fazer algo similar: ```python class UmaClasseQualquer: # métodos da classe, etc ################## # criar uma função qualquer def func(self, param): print(f'novo método: {param}') # adicioná-lo como método de uma classe já existente UmaClasseQualquer.novoMetodo = func x = UmaClasseQualquer() x.novoMetodo(42) # novo método: 42 ``` Mas diferente de Ruby, não dá para fazer com as classes nativas (como `int` ou `str`).
Na verdade quando a *feature* foi criada, a recomendação geral oficial era não colocar no `System` ou algo semelhante. Colocar global tende a ser pior ainda. Claro que para tudo tem exceção, mas para usar a exceção precisa dominar muito bem todas as consequências de usar isto, o que em projetos grandes pode ser um problema. Em JS é bem tranquilo porque é uma linguagem criada para pequenos *scripts*. As pessoas usam para projetos maiores por escolherem a ferramenta errada porque está todo mundo usando, que é o que chamamos de "modinha".

No C# eu uso porque é muito util. Você modificar uma classe sem precisar mexer no seu código é realmente poderoso. Já no JS não sabia que o protoype era semelhante. Aqui no Tabnews sempre aprendemos algo.

Extension methods é uma funcionalidade bem legal mesmo. Apesar de eu não usar C#, já tirei proveito disso em Kotlin.

Em TypeScript, para adicionar propriedades ao prototype de algum objeto global sem abrir mão da tipagem, vc pode usar uma interface. Por exemplo:

interface String {
  log(): void;
}

String.prototype.log = function () {
  console.log(this);
};

const text = "JavaScript Extension methods";
text.log();

O ideal é mover a declaração da interface para um arquivo *.d.ts para que a definição esteja disponível em qualquer módulo.

Puts!! Genial, eu não sabia massa que a cada dia o camarada aprende uma coisa nova! Mas Kotlin tem como fazer também? Não sabia! Outra coisa, java tem como fazer algo similar, já procurei a um tempo atrás quando precisei uma época atrás. Muito obrigado pela dica mestre!

Não são realmente conceitos equivalente, para além da descrição superficial de "estenderem um objeto". Extensions em C# é uma feature de linguagem que funciona no nivel real de tipos, o compilador detecta que você fez algo como something.ExtensionMethod() e transforma isso no equivalente funcional StaticClass.ExtensionMethod(something). Em JS não existem tipos no sentido concreto da palavra, a linguagem tem o conceito de prototypes pra permitir o compartilhamento de comportamentos entre objetos com raízes em comum, mas não passa disso. Você pode inclusive fazer coisas assim:

const prot = { click() { console.log('clicked') } };
const something = {};
something.click(); // erro
something.__PROTO__ = prot;
something.click(); // 'clicked'

Então está muito mais próximo de uma ideia de "herança" em OOP do que do que seriam os extension methods. Em C# você pode usar o type system ao máximo do potencial dele com as extensions, inclusive pra fazer coisas completamente genéricas como:

public static void PrintTypeName<T>(this T self)
    => Console.WriteLine($"Type name is {typeof(T).Name}");
"something".PrintTypeName();
Sim, sim! > você fez algo como something.ExtensionMethod() e transforma isso no equivalente funcional StaticClass.ExtensionMethod(something). Não só o compilador pode fazer isso, como você pode explicitamente fazer essa chamada, isso é um sulgar sintaxe da linguagem. Ai foi por isso que disse **similaridade**, a utilização é similar, o que muda é como a linguagem lida com isso. Obrigado, por agregar ao post!

Belo post! Uso muito Extension Methods no C#, as vezes até demais, mas é uma mão na roda incrível!