Executando verificação de segurança...
6

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

Carregando publicação patrocinada...
7

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

1

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!

5

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:

Array.prototype.novaFuncao = function() {
    // faz algo
};

var array = ['a', 'b'];
for (var i in array) {
    console.log(`${i} -> ${array[i]}`);
}

A saída é:

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

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

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

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

3

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

2

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.

2

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.

2

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!

2

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();
1

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!

1