При рассмотрении некоторых примеров кода для HtmlHelpers, и я вижу объявления, которые похожи:
public static string HelperName(this HtmlHelper htmlHelper, ...more regular params )
Я не могу помнить видеть этот тип конструкции кто-либо, где еще - кто-то может объяснить цель "этого"? Я думал, что путем объявления чего-то общедоступные помехи означали, что класс не должны были инстанцировать - поэтому, что "это" в этом случае?
Это синтаксис для объявления методов расширения, новой возможности 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();
Important: Класс, в котором объявлен метод расширения, должен быть доступен компилятору и процессору 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 в C# будет искать соответствующим образом названные методы расширения для объектов в игре, что означает, что вы можете "внедрить" поддержку Linq в любой тип класса, просто объявив правильные методы расширения. Конечно, полная поддержка Linq - это большая работа, но это возможно.
Кроме того, методы расширения сами по себе очень полезны, так что почитайте об этом.
Вот несколько ссылок:
После методов расширения я использовал их как сумасшедший .. вот несколько, которые я использую постоянно ..
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>();
Это будет метод расширения. Они позволяют «расширять» класс с помощью статических методов, находящихся вне исходного класса.
Например, у вас есть полезный строковый метод, который вы используете все время ...
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();
... это фантастический способ включить функциональность, как если бы вы использовали паттерн декоратор, но без необходимости рефакторинга всего кода или использования другого имени общего типа.
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 означает тип, к которому будет "добавлено" расширение, и позволяет работать со значением, как если бы оно было передано в качестве параметра.
Используется для методов расширения. Обычно вы «приклеиваете» имя помощника к объекту htmlHelper, чтобы вы могли сказать:
new HtmlHelper().HelperName(...more regular params);