Как расширить класс методами расширения c #?

98

Можно ли применить к классу методы расширения?

Например, расширите DateTime, чтобы включить метод Tomorrow (), который можно было бы вызывать следующим образом:

DateTime.Tomorrow();

Я знаю, что могу использовать

static DateTime Tomorrow(this Datetime value) { //... }

Или

public static MyClass {
  public static Tomorrow() { //... }
}

для аналогичного результата, но как я могу расширить DateTime, чтобы я мог вызывать DateTime.Tomorrow?

Дэвид Гленн
источник

Ответы:

69

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

Ничто не мешает вам создать собственный статический вспомогательный метод вроде этого:

static class DateTimeHelper
{
    public static DateTime Tomorrow
    {
        get { return DateTime.Now.AddDays(1); }
    }
}

Что бы вы использовали так:

DateTime tomorrow = DateTimeHelper.Tomorrow;
Эндрю Хэйр
источник
7
да ух? если это не было реализовано в течение 6 месяцев после этого и ответа Kumu прямо здесь, это выглядит на самом деле неполным!
cregox
4
@Cawas, это не неполно, Эндрю показывает, как это сделать с помощью статического помощника, а не с помощью метода расширения (поскольку экземпляра нет).
Ник Н.
1
Ты прав, Ник. Однако я предпочитаю методы расширения! ;)
cregox
2
Что насчет extensionmethod.net/csharp/datetime ? ИМХО, лучшие образцы для минимизации кривой обучения - это реальные приложения с полным исходным кодом и хорошими шаблонами
Kiquenet
3
Проблема с этим кодом в том, что он работает только с DateTime.Now, а не с любым объектом DateTime. В качестве служебной программы его можно использовать для определения дня, следующего за некоторым предыдущим (или будущим) днем. Не говоря уже о DateTime.Now определяется каждый раз, когда вы его вызываете ...
MintGrowth
182

Используйте метод расширения .

Пример:

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
            return date.AddDays(1);
        }    
    }
}

Использование:

DateTime.Now.Tomorrow();

или

AnyObjectOfTypeDateTime.Tomorrow();
Куму
источник
2
Shuggy ответ «s также измельчать некоторый свет на подобные пути решения этого.
cregox
8
Не забывайте "using ExtensionMethods;" в верхней части вашего документа для этого.
Люк Олдертон
почему я не могу сделать DateTime.Tomorrow ()?
lawphotog
Привет, lawphotog, этому расширению нужен объект, здесь DateTime - это структура, а не объект.
Kumu
4
Как упоминалось в предыдущих комментариях (очевидно, для меня это было недостаточно ясно), вы НЕ сможете использовать, DateTime.Tomorrow()поскольку методы расширения работают только с ЭКЗАМЕНАМИ класса и структуры класса. Чтобы «расширить» статический метод в структуре класса, следуйте ответам Эндрю или Шугги .
Alex
18

Методы расширения - это синтаксический сахар для создания статических методов, первый параметр которых является экземпляром типа T, похожим на метод экземпляра на T.

Таким образом, выгода в значительной степени теряется, когда вы создаете `` статические методы расширения '', поскольку они могли бы запутать читателя кода даже больше, чем метод расширения (поскольку они кажутся полностью квалифицированными, но фактически не определены в этом классе) без синтаксической выгоды (например, возможность связать вызовы в свободном стиле в Linq).

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

public static class DateTimeUtils
{
    public static DateTime Tomorrow { get { ... } }
}

А затем используйте это в своем коде через:

WriteLine("{0}", DateTimeUtils.Tomorrow)
ShuggyCoUk
источник
11

Наиболее близкий к ответу я могу дать ответ, добавив в System.Typeобъект метод расширения . Не очень красиво, но все же интересно.

public static class Foo
{
    public static void Bar()
    {
        var now = DateTime.Now;
        var tomorrow = typeof(DateTime).Tomorrow();
    }

    public static DateTime Tomorrow(this System.Type type)
    {
        if (type == typeof(DateTime)) {
            return DateTime.Now.AddDays(1);
        } else {
            throw new InvalidOperationException();
        }
    }
}

В противном случае у IMO Andrew и ShuggyCoUk есть лучшая реализация.

Адриан Годонг
источник
У этого подхода есть проблемы. Набирать typeof (...) неудобно, а с intellisense вы увидите расширения любого типа. Тем не менее, это интересный подход, о котором я не подумал, +1.
Meta-Knight
@ Мета-Рыцарь Верно, поэтому лично я предпочитаю ответ другого. Мой ответ будет иметь синтаксис, наиболее близкий к вопросу OP, но это не лучший способ решить эту проблему.
Адриан Годонг,
Typeможет быть заменен любым другим необходимым типом. Я использую его, Fromи он отлично работает. так что я предполагаю, что это общий, но правильный ответ
Катя
3

Я бы сделал то же самое, что и Куму

namespace ExtensionMethods
{
    public static class MyExtensionMethods
    {
        public static DateTime Tomorrow(this DateTime date)
        {
           return date.AddDays(1);
        }    
    }
}

но назовите это так: new DateTime (). Tomorrow ();

Думаю, это делает больше просмотров, чем DateTime.Now.Tomorrow ();

мимо
источник
1
И вы упустили возможность написать это как комментарий к ответу Kumu! : P
cregox
3

Они предоставляют возможность расширять существующие типы, добавляя новые методы без необходимости изменения типа. Вызов методов из объектов расширенного типа в приложении с использованием синтаксиса метода экземпляра известен как «расширяющие» методы. Методы расширения не являются членами экземпляра типа. Ключевой момент, о котором следует помнить, заключается в том, что методы расширения, определенные как статические методы, входят в область видимости только тогда, когда пространство имен явно импортируется в исходный код вашего приложения через директиву using. Несмотря на то, что методы расширения определены как статические методы, они по-прежнему вызываются с использованием синтаксиса экземпляра.

Посмотрите полный пример здесь http://www.dotnetreaders.com/articles/Extension_methods_in_C-sharp.net,Methods_in_C_-sharp/201

Пример:

class Extension
    {
        static void Main(string[] args)
        {
            string s = "sudhakar";
            Console.WriteLine(s.GetWordCount());
            Console.ReadLine();
        }

    }
    public static class MyMathExtension
    {

        public static int GetWordCount(this System.String mystring)
        {
            return mystring.Length;
        }
    }
Судхакар
источник
3

Я искал нечто подобное - список ограничений для классов, которые предоставляют методы расширения. Кажется, сложно найти краткий список, поэтому вот:

  1. У вас не может быть ничего личного или защищенного - полей, методов и т. Д.

  2. Это должен быть статический класс, например public static class....

  3. В классе могут быть только методы, и все они должны быть общедоступными статическими.

  4. У вас не может быть обычных статических методов - те, которые не включают аргумент this, не допускаются.

  5. Все методы должны начинаться:

    общедоступный статический ReturnType MethodName (this ClassName _this, ...)

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

Это создает неявную проблему - если вы добавляете методы, требующие блокировки любого вида, вы действительно не можете обеспечить ее на уровне класса. Обычно вы предоставляете частную блокировку на уровне экземпляра, но невозможно добавить какие-либо частные поля, что оставляет вам некоторые очень неудобные варианты, например, предоставление его в качестве общедоступной статики для некоторого внешнего класса и т. Д. Получается рискованно. Признаки того, что язык C # оказался плохим в их дизайне .

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

Крис Москини
источник
2

К сожалению, вы не можете этого сделать. Я считаю, что это было бы полезно. Естественнее набрать:

DateTime.Tomorrow

чем:

DateTimeUtil.Tomorrow

С классом Util вы должны проверить наличие статического метода в двух разных классах вместо одного.

Мета-Рыцарь
источник
1

Мы улучшили наш ответ подробным объяснением, теперь стало проще понять метод расширения

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

Мы можем расширить наши собственные классы, классы .NET Framework и т. Д.

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

Поскольку DateTimeкласс уже взят выше, мы не использовали этот класс для объяснения.

Ниже приведен пример

// Это существующий класс калькулятора, у которого есть только один метод (Добавить)

public class Calculator 
{
    public double Add(double num1, double num2)
    {
        return num1 + num2;
    }

}

// Below is the extension class which have one extension method.  
public static class Extension
{
    // It is extension method and it's first parameter is a calculator class.It's behavior is going to extend. 
    public static double Division(this Calculator cal, double num1,double num2){
       return num1 / num2;
    }   
}

// We have tested the extension method below.        
class Program
{
    static void Main(string[] args)
    {
        Calculator cal = new Calculator();
        double add=cal.Add(10, 10);
        // It is a extension method in Calculator class.
        double add=cal.Division(100, 10)

    }
}
Шео Дайал Сингх
источник