"this" в параметре функции

Посмотрите на некоторые примеры кода для HtmlHelpers, и я вижу объявления, которые выглядят так:

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

Я не помню, чтобы вид такого типа конструкции был где-то еще - может ли кто-нибудь объяснить цель "this"? Я думал, что, объявив что-то публичное статическое, означало, что класс не нужно создавать экземпляр - так что такое "this" в этом случае?

Ответ 1

Это синтаксис для объявления методов расширения, новая функция С# 3.0.

Метод расширения - это код части, компилятор части "магия", где компилятор с помощью intellisense в Visual Studio показывает, что ваш метод расширения фактически доступен как метод экземпляра для рассматриваемого объекта.

Позвольте мне привести пример.

В классе String, который называется GobbleGobble, нет метода, поэтому создайте метод расширения:

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

Имя класса - это просто мое соглашение об именах, нет необходимости называть его таким, но оно должно быть статическим, как и метод.

После объявления вышеуказанного метода вы можете в Visual Studio ввести следующее:

String s = "Turkey Baster!";
s.

после точки, дождитесь intellisense и обратите внимание, что там есть метод GobbleGobble, выполните следующий код:

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

Важно. Класс, в котором объявлен метод расширения, должен быть доступен компилятору и процессору intellisense для того, чтобы intellisense отображал этот метод. Если вы вручную вводите GobbleGobble и используете ярлык Ctrl + ., это не поможет вам получить правильное использование директив в файле.

Обратите внимание, что параметр метода исчез. Компилятор будет молча перемещаться по важным битам, которые:

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

Таким образом, приведенный выше код будет преобразован компилятором в следующее:

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

Итак, во время вызова нет ничего волшебного в этом, это просто вызов статического метода.

Обратите внимание, что если ваш метод расширения объявляет более одного параметра, только первый поддерживает модификатор this, а остальные должны быть указаны как часть вызова метода как обычно:

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

Методы расширения были добавлены частично из-за Linq, где синтаксис Linq С# будет искать подходящие именованные методы расширения для объектов в игре, что означает, что вы можете "внедрить" Linq-поддержку в любой тип класса, просто объявив правильные методы расширения. Конечно, полная поддержка Linq - это большая работа, но это возможно.

Кроме того, сами методы расширения действительно полезны, поэтому читайте на нем.

Вот несколько ссылок:

Ответ 2

Используется для методов расширения. В основном вы "приклеиваете" Helpername к объекту htmlHelper, чтобы вы могли сказать:

new HtmlHelper().HelperName(...more regular params);

Ответ 3

После методов расширения я использовал их как сумасшедшие.. вот несколько я использую постоянно.

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

Работает вот так.

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

Да, он появляется на каждом отдельном объекте, может быть раздражающим, но поскольку я использую его почти для каждого типа данных, он помогает просто прикрепить его к объекту, а не дублировать его для каждого типа данных.

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>();

Ответ 4

Это будет метод расширения. Они позволяют вам "расширять" класс с помощью статических методов, которые живут вне исходного класса.

Например, скажем, у вас есть полезный строковый метод, который вы используете все время...

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

И вы называете это...

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

Это не так уж плохо. Но с небольшим изменением вы можете сделать его методом расширения, и вызов будет немного красивее:

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

И затем назовите его...

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

Ответ 5

Методы расширений...

... являются фантастическим способом включить такие функции, как если бы вы использовали рисунок декоратора, но без боли при рефакторинге всех ваших кода или с использованием другого имени общего типа.

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

Таким образом, вы можете использовать этот код в любом месте приложения.

Console.WriteLine("Hello, My, Friend".RemoveComma())

>> Hello My Friend

Таким образом, атрибут this означает тип, который добавляется добавлением, и позволяет работать со значением, как если бы оно передавалось как параметр.