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

Métodos de extensão permitem que você "Adicionar" métodos para tipos existentes sem criar um novo derivado de tipo, recompilar ou modifique o tipo original de alguma forma. Métodos de extensão são um tipo especial de método estático, mas eles são chamados como se fossem os métodos de instância no tipo estendido. Para o código de cliente escrito em C# e Visual Basic, não existe aparente diferença entre chamar um método de extensão e os métodos que realmente são definidos em um tipo.

Os métodos de extensão mais comuns são o LINQ os operadores de consulta padrão que adicionam funcionalidade existente de consulta System.Collections.IEnumerable e tipos deSystem.Collections.Generic.IEnumerable<T> . Para usar os operadores de consulta padrão, primeiro trazê-las para o escopo com um using System.Linq diretiva. Em seguida, qualquer tipo que implementa IEnumerable<T> parece ter métodos de instância, como GroupBy, OrderBy, Averagee assim por diante. Você pode ver esses métodos adicionais na conclusão da instrução de IntelliSense quando você digita "dot" Depois de uma instância de um IEnumerable<T> tipo como List<T> ou Array.

O exemplo a seguir mostra como chamar o operador de consulta padrão OrderBy método em uma matriz de inteiros. A expressão entre parênteses é uma expressão lambda. As expressões lambda como parâmetros de levar muitos operadores de consulta padrão, mas isso não é um requisito para os métodos de extensão. Para obter mais informações, consulte Expressões lambda (guia de programação de 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

Os métodos de extensão são definidos como métodos estáticos, mas são chamados usando a sintaxe do método de instância. Seu primeiro parâmetro especifica qual tipo de método opera em, e o parâmetro é precedido pela Este modificador. Métodos de extensão são somente no escopo quando importar, explicitamente, o namespace para o seu código-fonte com um using diretiva.

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

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

O WordCount o método de extensão pode ser transferido para o escopo com este using diretiva:

using ExtensionMethods;

E pode ser chamado de um aplicativo usando esta sintaxe:

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

Em seu código, você chamar o método de extensão com a sintaxe do método de instância. No entanto, uma linguagem intermediária (IL) gerada pelo compilador converte seu código em uma chamada de método estático. Portanto, o princípio de encapsulamento não é realmente sendo violado. Na verdade, os métodos de extensão não podem acessar variáveis particulares no tipo que estejam estendendo.

Para obter mais informações, consulte How to: Implementar e chamar um método de extensão de Personalizar (guia de programação de C#).

Em geral, você irá provavelmente ser chamando métodos de extensão com muito mais freqüência do que implementar seu próprio. Porque os métodos de extensão são chamados usando a sintaxe do método de instância, nenhum conhecimento especial é necessário usá-los a partir do código do cliente. Para ativar métodos de extensão para um determinado tipo, basta adicionar uma using diretiva para o namespace no qual os métodos são definidos. Por exemplo, para usar os operadores de consulta padrão, adicione isso using diretiva ao seu código:

using System.Linq;

(Talvez você também precise adicionar uma referência a System.Core.dll.) Você irá notar que os operadores de consulta padrão aparecem agora na IntelliSense como métodos adicionais para a maioria das IEnumerable<T> tipos.

ObservaçãoObservação

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

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

Você pode usar os métodos de extensão para estender uma classe ou interface, mas não para substituí-las. Um método de extensão com o mesmo nome e assinatura como uma interface ou classe nunca será chamado. Em tempo de compilação, os métodos de extensão sempre têm prioridade menor do que os métodos de instância definida no próprio tipo. Em outras palavras, 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 irá sempre ligar para o método de instância. Quando o compilador encontra uma invocação de método, ele primeiro procura por uma correspondência nos métodos de instância do tipo. Se nenhuma correspondência for encontrada, ele irá procurar por quaisquer métodos de extensão que são definidos para o tipo e ligar para o primeiro método de extensão que encontrar. O exemplo a seguir demonstra como o compilador determina qual método de extensão ou o método de instância para vincular a.

Exemplo

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

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

Quando o compilador não consegue localizar um método de instância com uma assinatura correspondente, ele irá vincular a um método de extensão correspondente 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
            // nethod 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, é recomendável que você implemente métodos de extensão com moderação e somente quando necessário. Sempre que possível, o código do cliente deve estender um tipo existente deverá fazê-lo criando um novo tipo derivado do tipo existente. Para obter mais informações, consulte Inheritance (C# Programming Guide).

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

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

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

  • Métodos de extensão são trazidos para o escopo no nível do espaço para nome. Por exemplo, se você tiver várias classes estáticas que contêm os métodos de extensão em um único namespace chamado Extensions, eles serão todos sejam trazidos para o escopo pelo using Extensions; diretiva.

Consulte também

Referência

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

Conceitos

C# Programming Guide

Visão geral operadores de consulta padrão

Outros recursos

Conversão de regras, por exemplo, parâmetros e seu impacto

Métodos de extensão interoperabilidade entre linguagens

Métodos de extensão e representantes de Curried

Método de extensão de ligação e a emissão de relatórios de erro

Histórico de alterações

Date

History

Motivo

Outubro de 2010

Esclarecida o exemplo final.

Comentários do cliente.