“This” no parâmetro de função

88

Olhando alguns exemplos de código para HtmlHelpers, vejo declarações que se parecem com:

public static string HelperName(this HtmlHelper htmlHelper, ...more regular params )

Não me lembro de ter visto esse tipo de construção em qualquer outro lugar - alguém pode explicar o propósito do "isso"? Achei que declarar algo public static significava que a classe não precisava ser instanciada - então, o que é "this" neste caso?

Chris
fonte

Respostas:

215

Esta é a sintaxe para declarar métodos de extensão, um novo recurso do C # 3.0.

Um método de extensão é parte código, parte compilador "mágico", onde o compilador com a ajuda do intellisense no Visual Studio faz parecer que seu método de extensão está realmente disponível como um método de instância no objeto em questão.

Deixe-me dar um exemplo.

Não há método na classe String chamado GobbleGobble, então vamos criar um método de extensão:

public static class StringExtensions
{
    public static void GobbleGobble(this string s)
    {
        Console.Out.WriteLine("Gobble Gobble, " + s);
    }
}

O nome da classe é apenas minha convenção de nomenclatura, não é necessário nomeá-la assim, mas deve ser estática, assim como o método.

Depois de declarar o método acima, você pode, no Visual Studio, digitar o seguinte:

String s = "Turkey Baster!";
s.

após o ponto, espere pelo intellisense e observe que há um método GobbleGobble ali, complete o código assim:

String s = "Turkey Baster!";
s.GobbleGobble();

Importante : A classe onde o método de extensão é declarado deve estar disponível para o compilador e o processador do intellisense para que o intellisense mostre o método. Se você digitar GobbleGobble manualmente e usar o atalho Ctrl+ ., isso não o ajudará a acertar o uso de diretivas no arquivo.

Observe que o parâmetro para o método desapareceu. O compilador moverá silenciosamente os bits importantes, que são:

String s = "Turkey Baster!";
s.GobbleGobble();
^     ^
|     +-- the compiler will find this in the StringExtensions class
|
+-- will be used as the first parameter to the method

Assim, o código acima será transformado pelo compilador para este:

String s = "Turkey Baster!";
StringExtensions.GobbleGobble(s);

Portanto, no momento da chamada, não há nada de mágico nisso, é apenas uma chamada para um método estático.

Observe que se o seu método de extensão declara mais de um parâmetro, apenas o primeiro suporta o thismodificador, e o resto deve ser especificado como parte da chamada do método normalmente:

public static void GobbleGobble(this string value, string extra)
{                                            |              |
    ...                                      |              |
}                                            |              |
                                             |              |
+--------------------------------------------+              |
|                                                           |
v                                                           |
s.GobbleGobble("extra goes here");                          |
                        ^                                   |
                        |                                   |
                        +-----------------------------------+

Os métodos de extensão foram adicionados em parte devido ao Linq, onde a sintaxe Linq do C # procurará métodos de extensão nomeados apropriadamente para os objetos em jogo, o que significa que você pode "introduzir" o suporte ao Linq em qualquer tipo de classe apenas declarando a extensão correta métodos. Obviamente, o suporte completo ao Linq dá muito trabalho, mas é possível.

Além disso, os métodos de extensão por si só são realmente úteis, então leia sobre isso.

Aqui estão alguns links:

Lasse V. Karlsen
fonte
6
Definitivamente vou começar a usar o termo "Gobble Gobble Magic".
chris de
O Youtube quebrou o link novamente, youtube.com/watch?v=Bz_heb9Rz2g , ainda às 1h em diante.
Lasse V. Karlsen
Esse tipo de mágica do compilador torna difícil aprender uma linguagem.
Don Dilanga
8

Depois dos métodos de extensão, tenho usado-os como um louco .. aqui estão alguns que uso constantemente ..

public static T ChangeType<T>(this object obj)
{
  return (T)Convert.ChangeType(obj, typeof(T));
}

Funciona assim ..

int i = "123".ChangeType<int>();
bool valid = "bool".ChangeType<bool>();
int id = dataSet.Tables[0].Rows[0]["Id"].ChangeType<int>();

Sim, ele aparece em cada objeto, pode ser irritante, mas como eu uso isso para quase todos os tipos de dados, ajuda apenas anexar um objeto em vez de duplicá-lo para todos os tipos de dados possíveis.

public static string ToXml(this object serializableObject)
{
    var aMemStr = new MemoryStream();
    try
    {
        var serializer = new XmlSerializer(serializableObject.GetType());
        serializer.Serialize(new XmlTextWriter(aMemStr, null), serializableObject);
        return Encoding.UTF8.GetString(aMemStr.ToArray());
    }
    finally { if (aMemStr != null) { aMemStr.Dispose(); } }
}

string xml = dataSet.ToXml();

public static T ToObject<T>(this string xmlString)
{
    var aStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlString));
    try { return (T)new XmlSerializer(typeof(T)).Deserialize(aStream); }
    finally { if (aStream != null) { aStream.Dispose(); aStream = null; } }
}

DataSet dataSet = xml.ToObject<DataSet>();
Jaekie
fonte
6

É usado para métodos de extensão. Basicamente, você 'cola' o Helpername ao objeto htmlHelper para que possa dizer:

new HtmlHelper().HelperName(...more regular params);
Henrik Gering
fonte
4

Isso seria um método de extensão. Eles permitem que você "estenda" uma classe por meio de métodos estáticos que residem fora da classe original.

Por exemplo, digamos que você tenha um método de string útil que usa o tempo todo ...

public int CountAllAs(string orig)
{
    return orig.ToLowerInvariant().ToArray().Count(c => c == 'a');
}

E você chama isso ...

string allAs = "aaaA";
int count = CountAllAs(allAs);

Isso não é tão ruim. Mas com uma pequena mudança, você poderia torná-lo um método de extensão e a chamada seria um pouco mais bonita:

public static int CountAllAs(this string orig)
{
    return orig.ToLowerInvariant().ToArray().Count(c => c == 'a');
}

E então chamá-lo ...

string allAs = "aaaA";
int count = allAs.CountAllAs();
Justin Niessner
fonte
3

Métodos de extensões ...

... são uma maneira fantástica de incluir funcionalidades como se você estivesse usando o padrão decorator , mas sem a dor de refatorar todo o seu código, ou usar um nome diferente de um tipo comum.

public static class Extensions
{
     public static string RemoveComma(this string value)
     {
         if (value == null) throw new ArgumentNullException("value");
        return value.Replace(",", "");
    }
}  

Portanto, você pode usar este código em qualquer lugar em seu aplicativo.

Console.WriteLine(“Hello, My, Friend”.RemoveComma())

>> Hello My Friend

Portanto o atributo this command significa o tipo que a extensão será “adicionada”, e permite trabalhar com o valor como se fosse passado como parâmetro.

Fraga
fonte