Quais vantagens dos methods de extensão você encontrou?

Um “não crente” do C # estava me perguntando qual era o propósito dos methods de extensão. Eu expliquei que você poderia adicionar novos methods a objects que já estavam definidos, especialmente quando você não possui / controla a fonte para o object original.

Ele trouxe “Por que não apenas adicionar um método à sua própria aula?” Nós estamos dando voltas e voltas (no bom sentido). Minha resposta geral é que é outra ferramenta no cinturão de ferramentas, e sua resposta é que é um desperdício inútil de uma ferramenta … mas pensei em obter uma resposta mais “esclarecida”.

Quais são alguns cenários em que você usou methods de extensão que você não poderia ter (ou não deveria ter) usado um método adicionado à sua própria class?

Eu acho que os methods de extensão ajudam muito ao escrever código, se você adicionar methods de extensão a tipos básicos, você os obterá rapidamente no intellisense.

Eu tenho um provedor de formato para formatar um tamanho de arquivo . Para usá-lo eu preciso escrever:

 Console.WriteLine(String.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize)); 

Criando um método de extensão eu posso escrever:

 Console.WriteLine(fileSize.ToFileSize()); 

Mais limpo e mais simples.

A única vantagem dos methods de extensão é a legibilidade do código. É isso aí.

Os methods de extensão permitem que você faça isso:

 foo.bar(); 

em vez disso:

 Util.bar(foo); 

Agora há muitas coisas em C # que são assim. Em outras palavras, existem muitos resources em C # que parecem triviais e não têm grande benefício em si mesmos. No entanto, quando você começa a combinar esses resources, começa a ver algo um pouco maior do que a sum de suas partes. O LINQ se beneficia muito dos methods de extensão, pois as consultas do LINQ seriam quase ilegíveis sem elas. LINQ seria possível sem methods de extensão, mas não é prático.

Os methods de extensão são muito parecidos com as classs parciais do C #. Por si só, não são muito úteis e parecem triviais. Mas quando você começa a trabalhar com uma class que precisa de código gerado, as classs parciais começam a fazer muito mais sentido.

Não se esqueça de ferramentas! Quando você adiciona um método de extensão M no tipo Foo, você obtém ‘M’ na lista intellisense de Foo (supondo que a class de extensão esteja no escopo). Isso torna o ‘M’ muito mais fácil de encontrar do que o MyClass.M (Foo, …).

No final do dia, é apenas açúcar sintático para methods estáticos em outros lugares, mas como comprar uma casa: ‘localização, localização, localização!’ Se for do tipo, as pessoas vão encontrá-lo!

Mais dois benefícios dos methods de extensão que eu encontrei:

  • uma interface fluente pode ser encapsulada em uma class estática de methods de extensão, conseguindo assim uma separação de interesses entre a class principal e suas extensões fluentes; Eu vi que conseguir maior manutenção
  • Os methods de extensão podem ser suspensos das interfaces, permitindo que você especifique um contrato (por meio de uma interface) e uma série associada de comportamentos baseados em interface (por meio de methods de extensão), oferecendo novamente uma separação de interesses.

Alguns dos melhores usos que tive para methods de extensão é a capacidade de:

  1. Estenda a funcionalidade em objects de terceiros (sejam comerciais ou internos para minha empresa, mas gerenciados por um grupo separado), que em muitos casos serão marcados como sealed .
  2. Criar funcionalidade padrão para interfaces sem ter que implementar uma class abstrata

Tomemos por exemplo, IEnumerable . Embora seja rico em methods de extensão, achei irritante que ele não implementasse um método genérico de ForEach. Então, eu fiz o meu próprio:

 public void ForEach(this IEnumerable enumerable, Action action) { foreach ( var o in enumerable ) { action(o); } } 

Voila, todos os meus objects IEnumerable , independentemente do tipo de implementação, e se eu escrevi ou não alguém, agora tenho um método ForEach , adicionando uma instrução “using” apropriada no meu código.

Uma das grandes razões para usar methods de extensão é o LINQ. Sem methods de extensão, muito do que você pode fazer no LINQ seria muito difícil. Os methods de extensão Where (), Contains (), Select significam que muito mais funcionalidade é adicionada aos tipos existentes sem alterar sua estrutura.

Há muitas respostas sobre as vantagens dos methods de extensões; que tal um abordando as desvantagens ?

A maior desvantagem é que não há erro ou aviso do compilador se você tiver um método regular e um método de extensão com a mesma assinatura no mesmo contexto.

Suponha que você crie um método de extensão aplicado a uma determinada class. Então, mais tarde, alguém cria um método com uma assinatura idêntica na própria class.

Seu código irá compilar, e você pode até não receber um erro de execução. Mas você não está mais executando o mesmo código de antes.

Interfaces fluentes e sensibilidade ao contexto, conforme demonstrado por Greg Young no CodeBetter

Meu argumento pessoal para os methods de Extensão é que eles se encheckboxm muito bem em um design de OOP: considere o método simples

 bool empty = String.IsNullOrEmpty (myString) 

em comparação a

 bool empty = myString.IsNullOrEmpty (); 

Há muitas respostas excelentes sobre o que os methods de extensão permitem fazer.

Minha resposta curta é – eles quase eliminam a necessidade de fábricas.

Vou apenas salientar que eles não são um conceito novo e uma das maiores validações deles é que eles são um recurso matador em Objective-C ( categorias ). Eles adicionam tanta flexibilidade ao desenvolvimento baseado em frameworks que a NeXT teve como principais usuários os modeladores financeiros da NSA e de Wall Street.

O REALbasic também os implementa como methods de extensão e eles têm sido de uso similar, simplificando o desenvolvimento.

Gostaria de apoiar as outras respostas aqui que mencionam a melhor legibilidade do código como uma importante razão por trás dos methods de extensão. Demonstrarei isso com dois aspectos: encadeamento de método versus chamadas de método aninhadas e confusão de uma consulta LINQ com nomes de class estáticos sem sentido.


Vamos pegar essa consulta LINQ como exemplo:

 numbers.Where(x => x > 0).Select(x => -x) 

Tanto Where como Select são methods de extensão, definidos na class estática Enumerable . Assim, se os methods de extensão não existissem, e estes fossem methods estáticos normais, a última linha de código teria essencialmente a aparência de:

 Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x) 

Veja quanto mais desagradável essa consulta acabou de chegar.


Segundo, se você agora quisesse introduzir seu próprio operador de consulta, naturalmente não teria como defini-lo dentro da class estática Enumerable , como todos os outros operadores de consulta padrão, porque Enumerable está na estrutura e você não tem controle sobre essa class . Portanto, você teria que definir sua própria class estática contendo methods de extensão. Você pode então receber consultas como esta:

 Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x) // ^^^^^^^^^^^^^^^^^^^^^^ // different class name that has zero informational value // and, as with 'Enumerable.xxxxxx', only obstructs the // query's actual meaning. 

É verdade que você pode adicionar seu método (extensão) diretamente à sua class. Mas nem todas as classs são escritas por você. As classs da biblioteca principal ou bibliotecas de terceiros são frequentemente fechadas e seria impossível obter o açúcar sintático sem methods de extensão. Mas lembre-se, os methods de extensão são como methods autônomos (estáticos), por exemplo. c ++

Os methods de extensão também podem ajudar a manter suas classs e dependencies de class limpas. Por exemplo, você pode precisar de um método Bar () para a class Foo em todos os lugares em que Foo é usado. No entanto, você pode querer um método .ToXml () em outro assembly e somente para esse assembly. Nesse caso, você pode adicionar as dependencies System.Xml e / ou System.Xml.Linq necessárias nessa assembly e não na assembly original.

Benefícios: as dependencies na sua class de definição são reduzidas para apenas as necessidades básicas e outros assemblies consumidores serão impedidos de usar o método ToXml (). Veja esta apresentação do PDC para referência futura.

Os methods de extensão são realmente a incorporação .NET do refator “Introduce Foreign Method” do livro de Martin Fowler (até a assinatura do método). Eles vêm com basicamente os mesmos benefícios e armadilhas. Na seção sobre esse refatorador, ele diz que eles são uma solução para quando você não pode modificar a class que deve realmente possuir o método.

Eu vejo principalmente os methods de extensão como uma admissão de que talvez eles não devessem ter desaprovado funções gratuitas.

Na comunidade C ++, muitas vezes é considerado uma boa prática OOP preferir funções livres de não membros sobre os membros, porque essas funções não quebram o encapsulamento ao obter access a membros privados de que não precisam. Os methods de extensão parecem ser um caminho indireto para alcançar a mesma coisa. Ou seja, uma syntax mais limpa para funções estáticas que não têm access a membros privados.

Os methods de extensão não são nada além de açúcar sintático, mas não vejo mal algum em usá-los.

  • Intellisense no próprio object em vez de ter que chamar alguma function de utilidade feia
  • Para funções de conversão, pode-se alterar “XToY (X x)” para “ToY (este X x)”, o que resulta em um bonito x.ToY () em vez de um XToY (x) muito feio.
  • Estenda classs que você não tem controle sobre
  • Estenda a funcionalidade das classs quando for indesejável adicionar methods às próprias classs. Por exemplo, você pode manter objects de negócios simples e sem lógica e adicionar lógica de negócios específica com dependencies feias nos methods de extensão

Eu os uso para reutilizar minhas classs de modelo de object. Eu tenho um monte de classs que representam objects que eu tenho em um database. Essas classs são usadas no lado do cliente apenas para exibir os objects de modo que o uso básico esteja acessando as propriedades.

 public class Stock { public Code { get; private set; } public Name { get; private set; } } 

Devido a esse padrão de uso, não quero ter methods de lógica de negócios nessas classs, portanto, faço com que toda lógica de negócios seja um método de extensão.

 public static class StockExtender { public static List  GetQuotesByDate(this Stock s, DateTime date) {...} } 

Dessa forma, posso usar as mesmas classs para processamento de lógica de negócios e para exibição da interface do usuário sem sobrecarregar o lado do cliente com código desnecessário.

Uma coisa interessante sobre essa solução é que minhas classs de modelo de object são geradas dinamicamente usando o Mono.Cecil , então seria muito difícil adicionar methods de lógica de negócios, mesmo que eu quisesse. Eu tenho um compilador que lê arquivos de definição XML e gerar essas classs stubs representando algum object que tenho no database. A única abordagem neste caso é estendê-las.

Concordo que os methods de extensão aumentam a legibilidade do código, mas não são realmente nada além de methods auxiliares estáticos.

O IMO usando methods de extensão para adicionar comportamento às suas classs pode ser:

Confuso: os programadores podem acreditar que os methods fazem parte do tipo estendido, não entendendo por que os methods desapareceram quando o namespace de extensão não foi importado.

Um antipadrão: Você decide adicionar um comportamento a tipos em sua estrutura usando methods de extensão e, em seguida, envia-los para uma pessoa que faz parte do teste de unidade. Agora ele está preso a uma estrutura contendo vários methods que ele não pode falsificar.

Ele permite que o C # suporte melhor as linguagens dinâmicas, o LINQ e uma dúzia de outras coisas. Confira o artigo de Scott Guthrie .

No meu último projeto, usei o método de extensão para append methods Validate () a objects de negócios. Eu justifiquei isso porque os objects de negócios onde os dados serializáveis ​​transferem objects e serão usados ​​em diferentes domínios, como entidades de comércio eletrônico gerais, como produto, cliente, comerciante etc. Bem, em domínios diferentes, as regras de negócios também podem ser diferentes. lógica de validação de binding tardia em um método Validate attahced para a class base de meus objects de transferência de dados. Espero que isso faça sentido 🙂

Um caso em que os methods de extensão foram bastante úteis foi em um aplicativo cliente que usa serviços da web ASMX. Devido à serialização, os tipos de retorno de methods da Web não contêm nenhum método (apenas as propriedades públicas desses tipos estão disponíveis no cliente).

Os methods de extensão permitiam o uso para adicionar funcionalidade (no lado do cliente) aos tipos retornados pelos methods da Web sem ter que criar outro modelo de object ou várias classs de wrapper no lado do cliente.

Lembre-se também que os methods de extensão foram adicionados como uma forma de ajudar a consulta do Linq a ser mais legível, quando usado em seu estilo C #.

Essas duas afetações são absolutamente equivalentes, no entanto, a primeira é muito mais legível (e a lacuna na legibilidade, é claro, aumentaria com mais methods encadeados).

 int n1 = new List {1,2,3}.Where(i => i % 2 != 0).Last(); int n2 = Enumerable.Last(Enumerable.Where(new List {1,2,3}, i => i % 2 != 0)); 

Observe que a syntax totalmente qualificada deve ser:

 int n1 = new List {1,2,3}.Where(i => i % 2 != 0).Last(); int n2 = Enumerable.Last(Enumerable.Where(new List {1,2,3}, i => i % 2 != 0)); 

Por acaso, os parâmetros type de Where e Last não precisam ser explicitamente mencionados, pois podem ser inferidos graças à presença do primeiro parâmetro destes dois methods (o parâmetro que é introduzido pela palavra this chave this e torná-los methods de extensão ).

Este ponto é obviamente uma vantagem (entre outros) dos methods de extensão, e você pode tirar proveito dele em todos os cenários similares onde o encadeamento de methods está envolvido.

Especialmente, é a maneira mais elegante e convincente que encontrei de ter um método de class base invocável por qualquer subclass e retornando uma referência fortemente tipificada para essa subclass (com o tipo de subclass).

Exemplo (ok, este cenário é totalmente extravagante): depois de uma boa noite, um animal abre os olhos e dá um grito; todo animal abre os olhos da mesma maneira, enquanto um cachorro late e um pato kwaks.

 public abstract class Animal { //some code common to all animals } public static class AnimalExtension { public static TAnimal OpenTheEyes(this TAnimal animal) where TAnimal : Animal { //Some code to flutter one's eyelashes and then open wide return animal; //returning a self reference to allow method chaining } } public class Dog : Animal { public void Bark() { /* ... */ } } public class Duck : Animal { public void Kwak() { /* ... */ } } class Program { static void Main(string[] args) { Dog Goofy = new Dog(); Duck Donald = new Duck(); Goofy.OpenTheEyes().Bark(); //*1 Donald.OpenTheEyes().Kwak(); //*2 } } 

Conceitualmente, OpenTheEyes deveria ser um método Animal , mas retornaria uma instância da class abstrata Animal , que não conhece methods específicos de subclass, como Bark ou Duck ou o que quer que seja. As duas linhas comentadas como * 1 e * 2 criariam um erro de compilation.

Mas graças aos methods de extensão, podemos ter uma espécie de “método base que conhece o tipo de subclass no qual é chamado”.

Note que um método genérico simples poderia ter feito o trabalho, mas de uma maneira muito mais estranha:

 public abstract class Animal { //some code common to all animals public TAnimal OpenTheEyes() where TAnimal : Animal { //Some code to flutter one's eyelashes and then open wide return (TAnimal)this; //returning a self reference to allow method chaining } } 

Desta vez, nenhum parâmetro e, portanto, nenhuma inferência de tipo de retorno possível. A chamada pode ser nada diferente de:

 Goofy.OpenTheEyes().Bark(); Donald.OpenTheEyes().Kwak(); 

… o que pode pesar muito o código se mais encadeamento estiver envolvido (especialmente sabendo que o parâmetro type sempre será na linha de Goofy e no de Donald …)

Eu tenho apenas uma palavra para dizer sobre isso: MANUTENÇÃO Esta é a chave para o uso de methods de extensão

Eu acho que os methods de extensão ajudam a escrever código mais claro.

Em vez de colocar um novo método dentro da sua turma, como seu amigo sugeriu, você o coloca no namespace ExtensionMethods. Dessa forma, você mantém um senso lógico de ordem para sua class. Métodos que realmente não lidam diretamente com a sua turma não vão atrapalhar.

Sinto que os methods de extensão tornam seu código mais claro e organizado de maneira mais adequada.

Ele permite que seu editor / IDE faça uma sugestão automática completa.

Eu os amo por construir html. Freqüentemente há seções que são usadas repetidamente, ou geradas recursivamente onde uma function é útil, mas de outra forma quebraria o stream do programa.

  HTML_Out.Append("
    "); foreach (var i in items) if (i.Description != "") { HTML_Out.Append("
  • ") .AppendAnchor(new string[]{ urlRoot, i.Description_Norm }, i.Description) .Append("
    ") .AppendImage(iconDir, i.Icon, i.Description) .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("
  • "); } return HTML_Out.Append("
").ToString();

Há também situações em que um object precisa de lógica customizada para ser preparado para methods de extensão de saída HTML, permitindo que você adicione essa funcionalidade sem misturar apresentação e lógica dentro da class.

Eu encontrei methods de extensão são úteis para coincidir com argumentos genéricos nesteds.

Isso soa um pouco estranho – mas digamos que temos uma class genérica MyGenericClass , e sabemos que a própria TList é genérica (por exemplo, uma List ), não acho que exista uma maneira de extrair essa class aninhada ‘ T ‘da Lista sem methods de extensão ou methods auxiliares estáticos. Se tivermos apenas methods auxiliares estáticos à nossa disposição, isso será (a) feio e (b) nos forçará a mover a funcionalidade que pertence à class para um local externo.

por exemplo, para recuperar os tipos em uma tupla e convertê-los em uma assinatura de método, podemos usar methods de extensão:

 public class Tuple { } public class Tuple : Tuple { } public class Tuple : Tuple { } public class Caller where TTuple : Tuple { /* ... */ } public static class CallerExtensions { public static void Call(this Caller> caller, T0 p0) { /* ... */ } public static void Call(this Caller> caller, T0 p0, T1 p1) { /* ... */ } } new Caller>().Call(10); new Caller>().Call("Hello", 10); 

Dito isso, não tenho certeza de onde a linha divisória deve ser – quando um método deveria ser um método de extensão e quando deveria ser um método auxiliar estático? Alguma ideia?

Os methods de extensão podem ser usados ​​para criar um tipo de mixin em C #.

Isso, por sua vez, fornece uma melhor separação de interesses para conceitos ortogonais. Dê uma olhada nesta resposta como um exemplo.

Isso também pode ser usado para ativar funções em C #, um conceito central para a arquitetura DCI .

Eu tenho zonas de input na minha canvas, e todas devem implementar um comportamento padrão, quaisquer que sejam seus tipos exatos (checkboxs de texto, checkboxs de seleção, etc.). Eles não podem herdar uma class base comum, já que cada tipo de zona de input já deriva de uma class específica (TextInputBox, etc.)

Talvez subindo na hierarquia de inheritance eu poderia encontrar um ancestral comum como o WebControl, mas eu não desenvolvi o framework da class WebControl e ele não expõe o que eu preciso.

Com o método de extensão, posso:

1) estender a class WebControl e, em seguida, obter meu comportamento padrão unificado em todas as minhas classs de input

2) alternativamente, faça todas as minhas classs derivarem de uma interface, digamos IInputZone, e estenda essa interface com methods. Agora poderei chamar methods de extensões relacionados à interface em todas as minhas zonas de input. Assim, consegui um tipo de inheritance múltipla, já que minhas zonas de input já derivavam de múltiplas classs de base.

Existem muitos exemplos excelentes de methods de extensão … especialmente em IEnumerables como postado acima.

por exemplo, se eu tenho um IEnumerable eu posso criar e método de extensão para IEnumerable

 mylist List; 

… crie a lista

 mylist.DisplayInMyWay(); 

Sem os methods de extensão, você deve chamar:

 myDisplayMethod(myOldArray); // can create more nexted brackets. 

Outro ótimo exemplo é criar uma linked list circular em um flash!

Eu posso ‘tomar crédito por isso!

linked list circlular usando methods de extensão

Agora combine-os e usando o código de Métodos de Extensão, leia como segue.

 myNode.NextOrFirst().DisplayInMyWay(); 

ao invés de

 DisplayInMyWay(NextOrFirst(myNode)). 

Usando methods de extensão É mais simples e mais fácil de ler e mais orientada a objects. também muito perto de:

 myNode.Next.DoSomething() 

Mostre isso ao seu colega! 🙂