Compartilhar via


Métodos de extensão (guia de programação do C#)

Métodos de extensão “” permite que você adicione métodos para tipos existentes sem criar um novo tipo derivado, recompile, ou caso contrário alterar o tipo original.Métodos de extensão é um tipo especial do método estático, mas são chamados como se fossem métodos de instância no tipo estendido.Para o código do cliente escrito em C# e Visual Basic, não há nenhuma diferença entre aparente chamar um método de extensão e métodos que são definidos na verdade um tipo.

Os métodos mais comuns de extensão estão os operadores de consulta padrão de LINQ que adicionam funcionalidade de consulta a System.Collections.IEnumerable e tipos existentes de System.Collections.Generic.IEnumerable<T> . Para usar os operadores de consulta padrão, traga-os principalmente no escopo com uma política de using System.Linq .Em qualquer tipo que implementa IEnumerable<T> parece ter métodos de instância como GroupBy, OrderBy, Average, e assim por diante.Você pode ver esses métodos adicionais na conclusão da instrução do IntelliSense quando você digita o ponto “” após uma instância de um tipo de IEnumerable<T> como List<T> ou Array.

O exemplo a seguir mostra como chamar o método padrão de OrderBy de operador de consulta em uma matriz de inteiros.A expressão entre parênteses é uma expressão lambda.Muitos operadores de consulta padrão têm expressões lambda como parâmetros, mas isso não é um requisito para métodos de extensão.Para obter mais informações, consulte Expressões lambda (guia de programação do C#).

class ExtensionMethods2    
{

    static void Main()
    {            
        int[] ints = { 10, 45, 15, 39, 21, 26 };
        var result = ints.OrderBy(g => g);
        foreach (var i in result)
        {
            System.Console.Write(i + " ");
        }           
    }        
}
//Output: 10 15 21 26 39 45

Métodos de extensão são definidos como métodos estáticos mas chamados usando a sintaxe método de instância.O primeiro parâmetro especifica que tipo o método operam em, e o parâmetro é precedido pelo modificador de isso .Métodos de extensão são apenas no escopo quando você importar o namespace explicitamente em seu código-fonte com uma política de using .

O exemplo a seguir mostra um método de extensão definido para a classe de System.String .Observe que ele é definido dentro uma classe estática não aninhada, não genéricos:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int WordCount(this String str)
        {
            return str.Split(new char[] { ' ', '.', '?' }, 
                             StringSplitOptions.RemoveEmptyEntries).Length;
        }
    }   
}

O método de extensão de WordCount pode ser colocado no escopo com esta política de using :

using ExtensionMethods;

E pode ser chamado de um aplicativo usando essa sintaxe:

string s = "Hello Extension Methods";
int i = s.WordCount();

No seu código você chama o método de extensão com sintaxe método de instância.No entanto, a linguagem intermediária (IL) gerado pelo compilador converte seu código chame o método estático.Como consequência, o princípio de encapsulação não está sendo realmente violado.Na verdade, os métodos de extensão não podem acessar as variáveis particulares no tipo que estão estendendo.

Para obter mais informações, consulte Como: implementar e chamar um método de extensão personalizada (guia de programação do C#).

Geralmente, você provavelmente será chamando métodos de extensão distante com mais freqüência de implementar seus próprios.Como os métodos de extensão são chamados usando a sintaxe método de instância, nenhum conhecimento especial é necessário para usá-los código do cliente.Para ativar métodos de extensão para um tipo específico, adicionar apenas uma política de using para o namespace em que os métodos são definidos.Por exemplo, para usar os operadores de consulta padrão, adicionar esta política de using ao seu código:

using System.Linq;

(Você pode também precise adicionar uma referência a System.Core.dll.) Você notará que os operadores de consulta padrão no IntelliSense agora aparecem como métodos adicionais disponíveis para a maioria de IEnumerable<T> necessários.

ObservaçãoObservação

Embora os operadores de consulta padrão não aparece no IntelliSense para String, ainda estão disponíveis.

Métodos de extensão de associação em tempo de compilação

Você pode usar métodos de extensão para estender uma classe ou interface, mas não para os substituir.Um método de extensão com o mesmo nome e assinatura de um método de interface ou classe nunca será chamado.Em tempo de compilação, os métodos de extensão sempre têm a prioridade inferior de que os métodos de instância definidos no próprio tipo.Ou seja se um tipo tem um método chamado Process(int i), e você tem um método de extensão com a mesma assinatura, o compilador associar-se-&z sempre para o método de instância.Quando o compilador encontra uma chamada de método, primeiro procura por uma correspondência em métodos da instância do tipo.Se nenhuma correspondência for encontrada, irá procurar por todos os métodos de extensão que são definidos para o tipo, e pela para o primeiro método de extensão que encontrar.O exemplo a seguir demonstra como o compilador determina que método de extensão ou método da instância para associar.

Exemplo

O exemplo a seguir demonstra as regras que o compilador C# maneira de determinar se associar um chamada de método a um método de instância no tipo, ou método de extensão.A classe estática Extensions contém métodos de extensão definidos para qualquer tipo que implementa IMyInterface.Classes A, B, e C qualquer implementam a interface.

O método de extensão de MethodB nunca é chamado porque seu nome e assinatura correspondem exatamente os métodos já implementados pela classe.

Quando o compilador não pode localizar um método de instância com uma assinatura compatível, associar-se-&z a um método correspondente de extensão se houver.

// Define an interface named IMyInterface.
namespace DefineIMyInterface
{
    using System;

    public interface IMyInterface
    {
        // Any class that implements IMyInterface must define a method
        // that matches the following signature.
        void MethodB();
    }
}


// Define extension methods for IMyInterface.
namespace Extensions
{
    using System;
    using DefineIMyInterface;

    // The following extension methods can be accessed by instances of any 
    // class that implements IMyInterface.
    public static class Extension
    {
        public static void MethodA(this IMyInterface myInterface, int i)
        {
            Console.WriteLine
                ("Extension.MethodA(this IMyInterface myInterface, int i)");
        }

        public static void MethodA(this IMyInterface myInterface, string s)
        {
            Console.WriteLine
                ("Extension.MethodA(this IMyInterface myInterface, string s)");
        }

        // This method is never called in ExtensionMethodsDemo1, because each 
        // of the three classes A, B, and C implements a method named MethodB
        // that has a matching signature.
        public static void MethodB(this IMyInterface myInterface)
        {
            Console.WriteLine
                ("Extension.MethodB(this IMyInterface myInterface)");
        }
    }
}


// Define three classes that implement IMyInterface, and then use them to test
// the extension methods.
namespace ExtensionMethodsDemo1
{
    using System;
    using Extensions;
    using DefineIMyInterface;

    class A : IMyInterface
    {
        public void MethodB() { Console.WriteLine("A.MethodB()"); }
    }

    class B : IMyInterface
    {
        public void MethodB() { Console.WriteLine("B.MethodB()"); }
        public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); }
    }

    class C : IMyInterface
    {
        public void MethodB() { Console.WriteLine("C.MethodB()"); }
        public void MethodA(object obj)
        {
            Console.WriteLine("C.MethodA(object obj)");
        }
    }

    class ExtMethodDemo
    {
        static void Main(string[] args)
        {
            // Declare an instance of class A, class B, and class C.
            A a = new A();
            B b = new B();
            C c = new C();

            // For a, b, and c, call the following methods:
            //      -- MethodA with an int argument
            //      -- MethodA with a string argument
            //      -- MethodB with no argument.

            // A contains no MethodA, so each call to MethodA resolves to 
            // the extension method that has a matching signature.
            a.MethodA(1);           // Extension.MethodA(object, int)
            a.MethodA("hello");     // Extension.MethodA(object, string)

            // A has a method that matches the signature of the following call
            // to MethodB.
            a.MethodB();            // A.MethodB()

            // B has methods that match the signatures of the following
            // method calls.
            b.MethodA(1);           // B.MethodA(int)
            b.MethodB();            // B.MethodB()

            // B has no matching method for the following call, but 
            // class Extension does.
            b.MethodA("hello");     // Extension.MethodA(object, string)

            // C contains an instance method that matches each of the following
            // method calls.
            c.MethodA(1);           // C.MethodA(object)
            c.MethodA("hello");     // C.MethodA(object)
            c.MethodB();            // C.MethodB()
        }
    }
}
/* Output:
    Extension.MethodA(this IMyInterface myInterface, int i)
    Extension.MethodA(this IMyInterface myInterface, string s)
    A.MethodB()
    B.MethodA(int i)
    B.MethodB()
    Extension.MethodA(this IMyInterface myInterface, string s)
    C.MethodA(object obj)
    C.MethodA(object obj)
    C.MethodB()
 */

Diretrizes gerais

Em geral, recomendamos que você implementar métodos de extensão com parcimônia e somente quando você precisa.Sempre que possível, o código do cliente que deve estender um tipo existente devem fazer isso criando um novo tipo derivado do tipo existente.Para obter mais informações, consulte Herança (guia de programação do C#).

Ao usar um método de extensão para estender um tipo cujo código fonte você não pode modificar, você corra o risco de uma alteração na implementação do tipo fará com que o método de extensão interrompa.

Se você implementar métodos de extensão para um determinado tipo, lembre-se os followingpoints:

  • Um método de extensão será chamado se nunca tem a mesma assinatura de um método definido no tipo.

  • Métodos de extensão são trazidos no escopo no namespace.Por exemplo, se você tiver várias classes estáticos que contêm métodos de extensão em uma única chamada Extensionsnamespace, tudo serão trazidos no escopo pela política de using Extensions; .

Para uma biblioteca de classes que você implementa, você não deve usar métodos de extensão para evitar incrementar o número de versão de um assembly.Se você deseja adicionar a funcionalidade significativa a uma biblioteca que você possui o código-fonte, você deve seguir as diretrizes padrão do .NET Framework para o controle de versão do assembly.Para obter mais informações, consulte Versionamento de assembly.

Consulte também

Referência

Expressões lambda (guia de programação do C#)

Conceitos

Guia de programação do C#

Visão geral operadores de consulta padrão

Outros recursos

Parâmetros das regras de conversão por exemplo e seu impacto

Métodos de extensão interoperabilidade entre linguagens

Métodos de extensão e representantes surrados

Associar e relatório de erros do método de extensão