«This» в параметре функции


88

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

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

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

Ответы:


212

Это синтаксис для объявления методов расширения, новой функции C # 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. сочетанием клавиш + , это не поможет вам получить права using в файле.

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

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 C # будет искать методы расширения с соответствующими именами для объектов в игре, что означает, что вы можете «внедрить» поддержку Linq в любой тип класса, просто объявив правильное расширение. методы. Конечно, полная поддержка Linq - это большая работа, но это возможно.

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

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


6
Я определенно собираюсь начать использовать термин «Gobble Gobble Magic».
Крис

Youtube снова разорвал ссылку, youtube.com/watch?v=Bz_heb9Rz2g , все еще в 1:00 и позже.
Лассе В. Карлсен

Такая магия компилятора затрудняет изучение языка.
Дон Диланга,

8

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

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

6

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

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

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

3

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

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

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 означает тип, к которому будет «добавлено» расширение, и позволяет работать со значением, как если бы оно было передано как параметр.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.