Eu vi recentemente uma base de código que tinha uma classe de dados Address
definida em algum lugar e depois em um lugar diferente:
fun Address.toAnschrift() = let { address ->
Anschrift().apply {
// mapping code here...
}
}
Achei confuso não ter esse método no endereço diretamente. Existem padrões estabelecidos ou práticas recomendadas quando usar métodos de extensão? Ou o livro ainda precisa ser escrito?
Observe que, apesar da sintaxe Kotlin no meu exemplo, estou interessado nas melhores práticas gerais, pois também se aplicaria ao C # ou a outros idiomas com esses recursos.
Respostas:
Os métodos de extensão não fornecem algo que não possa ser feito de outras maneiras. Eles são açúcar sintático para tornar o desenvolvimento mais agradável, sem fornecer um benefício técnico real.
Os métodos de extensão são relativamente novos. Padrões e convenções geralmente são decididos pela opinião popular (além de alguma experiência de longo prazo sobre o que acaba sendo uma má idéia), e não acho que estamos no ponto em que podemos traçar uma linha definitiva com a qual todos concordam.
No entanto, posso ver vários argumentos, com base nas coisas que ouvi de colegas de trabalho.
1. Eles geralmente estão substituindo métodos estáticos.
Os métodos de extensão são mais comumente baseados em coisas que seriam métodos estáticos, não métodos de classe. Eles se parecem com métodos de classe, mas na verdade não são.
Métodos de extensão simplesmente não devem ser considerados uma alternativa para métodos de classe; mas como uma maneira de fornecer aos métodos estáticos sintaticamente alternados uma aparência e um "método de classe".
2. Quando o método pretendido for específico para um projeto consumidor, mas não a biblioteca de origem.
Só porque você desenvolve a biblioteca e o consumidor não significa que você está disposto a colocar a lógica onde quer que ela se encaixe.
Por exemplo:
PersonDto
turma vinda do seuDataLayer
projeto.WebUI
projeto deseja converter umPersonDto
para umPersonViewModel
.Você não pode (e não gostaria de) adicionar esse método de conversão ao seu
DataLayer
projeto. Como esse método está no escopo doWebUI
projeto, ele deve residir dentro desse projeto.Um método de extensão permite que você tenha esse método acessível globalmente dentro do seu projeto (e possíveis consumidores do seu projeto), sem exigir que a
DataLayer
biblioteca o implemente.3. Se você acha que as classes de dados devem conter apenas dados.
Por exemplo, sua
Person
classe contém as propriedades para uma pessoa. Mas você deseja ter alguns métodos que formatam alguns dos dados:Eu já vi muitos colegas de trabalho que odeiam a idéia de misturar dados e lógica em uma classe. Não concordo com coisas simples, como formatação de dados, mas admito que, no exemplo acima, parece sujo
Person
ter que dependerSecurityQuestionAnswers
.Colocar esse método em um método de extensão evita o manuseio da classe de dados pura.
Observe que esse argumento é de estilo. Isso é semelhante às classes parciais. Há pouco ou nenhum benefício técnico em fazê-lo, mas ele permite que você separe o código em vários arquivos se você o considerar mais limpo (por exemplo, se muitas pessoas usarem a classe, mas não se importarem com seus métodos personalizados adicionais).
4. Métodos auxiliares
Na maioria dos projetos, acabo tendo aulas auxiliares. Essas são classes estáticas que geralmente fornecem uma coleção de métodos para facilitar a formatação.
Por exemplo, uma empresa em que trabalhei tinha um formato de data e hora específico que eles queriam usar. Em vez de colar a string de formato em todo o lugar ou transformar a string de formato em uma variável global, optei pelo método de extensão DateTime:
Isso é melhor do que um método auxiliar estático normal? Acho que sim. Ele limpa a sintaxe. Ao invés de
Eu poderia fazer:
Isso é semelhante a uma versão fluente da mesma sintaxe. Gosto mais, mesmo que não exista nenhum benefício técnico em ter um sobre o outro.
Todos esses argumentos são subjetivos. Nenhum deles cobre um caso que nunca poderia ser coberto.
Mas, novamente, podemos aceitar sua afirmação de que eles nunca são necessários ao extremo:
A resposta a esta pergunta, e a sua, permanece a mesma:
Uma menção extra específica:
fonte
Concordo com Flater que não houve tempo suficiente para a convenção se cristalizar, mas temos o exemplo das próprias bibliotecas de classes do .NET Framework. Considere que, quando os métodos LINQ foram adicionados ao .NET, eles não foram adicionados diretamente
IEnumerable
, mas como métodos de extensão.O que podemos tirar disso? LINQ é
A.B().C()
vez deC(B(A))
) eSe você tiver todos esses três recursos, os métodos de extensão farão muito sentido. De fato, eu argumentaria que, se um conjunto de métodos tiver o recurso nº 3 e um dos dois primeiros, considere torná-los métodos de extensão.
Métodos de extensão:
null
valores de maneira mais flexível , pois um método de extensão chamado de umnull
valor simplesmente recebe onull
primeiro argumento,Os métodos de extensão têm mais um benefício: eles ainda podem ser usados como um método estático e podem ser passados diretamente como um valor para uma função de ordem superior. Então, se
MyMethod
é um método de extensão, em vez de dizermyList.Select(x => x.MyMethod())
, você pode simplesmente usarmyList.Select(MyMethod)
. Eu acho isso melhor.fonte
A força motriz por trás dos métodos de extensão é a capacidade de adicionar a funcionalidade necessária a todas as classes ou interfaces de um determinado tipo, independentemente de estarem seladas ou em outra montagem. Você pode ver evidências disso com o código LINQ, adicionando funções a todos os
IEnumerable<T>
objetos, sejam elas listas ou pilhas, etc. O conceito era a prova futura das funções, para que, se você tivesse criado o seu próprio,IEnumerable<T>
pudesse usá-lo automaticamente.Dito isto, o recurso de idioma é novo o suficiente para que você não tenha nenhuma orientação sobre quando usá-los ou não. No entanto, eles permitem várias coisas anteriormente impossíveis:
Para ser justo, apenas o tempo dirá a que distância está longe demais ou em que momento os métodos de extensão se tornam um obstáculo, e não um benefício. Como mencionado em outra resposta, não há nada em um método de extensão que não possa ser feito com um método estático. No entanto, quando usados criteriosamente, podem facilitar a leitura do código.
Que tal usar intencionalmente métodos de extensões no mesmo assembly?
Eu acho que vale a pena ter a funcionalidade principal bem testada e confiável, e depois não mexer com ela até que você precise. O novo código que depende dele, mas precisa fornecer funcionalidade exclusivamente para o benefício do novo código, pode usar os Métodos de extensão para adicionar quaisquer recursos para dar suporte ao novo código. Essas funções são de interesse apenas para o novo código, e o escopo dos métodos de extensão é limitado ao espaço para nome em que são declarados.
Eu não descartaria cegamente o uso de métodos de extensão, mas consideraria se a nova funcionalidade deve ser realmente adicionada ao código principal existente que é bem testado.
fonte
Um dos motivos mais comuns para o uso de métodos de extensão é como um meio de "estender, não modificar" interfaces. Em vez de ter
IFoo
eIFoo2
, onde este introduz um novo métodoBar
,Bar
é adicionado aIFoo
um método de extensão.Uma das razões pelas quais o Linq usa métodos de extensão
Where
,Select
etc , é para permitir que os usuários definam suas próprias versões desses métodos, que também são usados pela sintaxe de consulta do Linq.Além disso, a abordagem que eu uso, que parece se encaixar no que normalmente vejo na estrutura .NET, é:
Então, se eu tenho um
Option<T>
, eu ia colocar coisas comoHasValue
,Value
,==
, etc dentro do próprio tipo. Mas algo como umaMap
função, que converte a mônada deT1
paraT2
, eu colocaria um método de extensão:fonte
One of the reasons Linq uses extension methods for Where, Select etc is in order to allow users to define their own versions of these methods, which are then used by the Linq query syntax too.
-- Você tem certeza sobre isso? A sintaxe do Linq faz uso extensivo de expressões lambda (ou seja, funções de primeira classe), para que não seja necessário escrever suas próprias versões deSelect
eWhere
. Não conheço nenhum caso em que as pessoas estejam lançando suas próprias versões dessas funções.if (!predicate(item))
código paraif (predicate(item))
, os resultados mudam à medida que minha versão doWhere
.