Могу ли я добавить методы расширения в существующий статический класс? - PullRequest
490 голосов
/ 30 октября 2008

Я фанат методов расширения в C #, но не смог успешно добавить метод расширения в статический класс, такой как Console.

Например, если я хочу добавить в консоль расширение, называемое «WriteBlueLine», чтобы я мог перейти:

Console.WriteBlueLine("This text is blue");

Я попробовал это, добавив локальный, публичный статический метод, с консолью в качестве параметра 'this' ... но без кубиков!

public static class Helpers {
    public static void WriteBlueLine(this Console c, string text)
    {
        Console.ForegroundColor = ConsoleColor.Blue;
        Console.WriteLine(text);
        Console.ResetColor();
    }
}

Это не добавило метод 'WriteBlueLine' в Консоль ... я делаю это неправильно? Или просить невозможного?

Ответы [ 15 ]

257 голосов
/ 21 ноября 2008

Нет. Методы расширения требуют переменную экземпляра (значение) для объекта. Однако вы можете написать статическую оболочку для интерфейса ConfigurationManager. Если вы реализуете обертку, вам не нужен метод расширения, поскольку вы можете просто добавить метод напрямую.

 public static class ConfigurationManagerWrapper
 {
      public static ConfigurationSection GetSection( string name )
      {
         return ConfigurationManager.GetSection( name );
      }

      .....

      public static ConfigurationSection GetWidgetSection()
      {
          return GetSection( "widgets" );
      }
 }
86 голосов
/ 27 марта 2011

Можете ли вы добавить статические расширения к классам в C #? Нет, но вы можете сделать это:

public static class Extensions
{
    public static T Create<T>(this T @this)
        where T : class, new()
    {
        return Utility<T>.Create();
    }
}

public static class Utility<T>
    where T : class, new()
{
    static Utility()
    {
        Create = Expression.Lambda<Func<T>>(Expression.New(typeof(T).GetConstructor(Type.EmptyTypes))).Compile();
    }
    public static Func<T> Create { get; private set; }
}

Вот как это работает. Хотя технически вы не можете написать статические методы расширения, вместо этого этот код использует лазейку в методах расширения. Эта лазейка заключается в том, что вы можете вызывать методы расширения для нулевых объектов, не получая нулевое исключение (если вы не обращаетесь к чему-либо через @this).

Итак, вот как бы вы использовали это:

    var ds1 = (null as DataSet).Create(); // as oppose to DataSet.Create()
    // or
    DataSet ds2 = null;
    ds2 = ds2.Create();

    // using some of the techniques above you could have this:
    (null as Console).WriteBlueLine(...); // as oppose to Console.WriteBlueLine(...)

Теперь, ПОЧЕМУ я выбрал вызов конструктора по умолчанию в качестве примера, и И почему я не могу просто вернуть new T () в первом фрагменте кода, не выполнив весь этот мусор Expression? Ну, сегодня ваш счастливый день, потому что вы получаете 2fer. Как знает любой продвинутый разработчик .NET, new T () работает медленно, поскольку генерирует вызов System.Activator, который использует отражение, чтобы получить конструктор по умолчанию перед вызовом. Черт бы тебя побрал, Microsoft! Однако мой код вызывает конструктор объекта по умолчанию напрямую.

Статические расширения были бы лучше, чем это, но отчаянные времена требуют отчаянных мер.

43 голосов
/ 12 января 2009

Это невозможно.

И да, я думаю, что MS допустил ошибку здесь.

Их решение не имеет смысла и вынуждает программистов писать (как описано выше) бессмысленный класс-обертку.

Вот хороший пример: Попытка расширить статический класс модульного тестирования MS Assert: я хочу еще 1 метод Assert AreEqual(x1,x2).

Единственный способ сделать это - указать на разные классы или написать обертку вокруг сотен различных методов Assert. Почему!?

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

17 голосов
/ 01 июня 2017

Я наткнулся на эту тему, пытаясь найти ответ на тот же вопрос, который имел ОП. Я не нашел ответ, который хотел, но в итоге я сделал это.

public static class MyConsole
{
    public static void WriteLine(this ConsoleColor Color, string Text)
    {
        Console.ForegroundColor = Color;
        Console.WriteLine(Text);   
    }
}

И я использую это так:

ConsoleColor.Cyan.WriteLine("voilà");
17 голосов
/ 07 января 2010

Может быть, вы могли бы добавить статический класс со своим пользовательским пространством имен и тем же именем класса:

using CLRConsole = System.Console;

namespace ExtensionMethodsDemo
{
    public static class Console
    {
        public static void WriteLine(string value)
        {
            CLRConsole.WriteLine(value);
        }

        public static void WriteBlueLine(string value)
        {
            System.ConsoleColor currentColor = CLRConsole.ForegroundColor;

            CLRConsole.ForegroundColor = System.ConsoleColor.Blue;
            CLRConsole.WriteLine(value);

            CLRConsole.ForegroundColor = currentColor;
        }

        public static System.ConsoleKeyInfo ReadKey(bool intercept)
        {
            return CLRConsole.ReadKey(intercept);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteBlueLine("This text is blue");   
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);
            }

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey(true);
        }
    }
}
10 голосов
/ 21 ноября 2008

Неа. Для определения метода расширения требуется экземпляр типа, который вы расширяете. Это неудачно; Я не уверен, почему это требуется ...

9 голосов
/ 28 сентября 2017
7 голосов
/ 11 января 2012

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

г. Obnoxious пишет: «Как знает любой продвинутый разработчик .NET, новый T () работает медленно, поскольку генерирует вызов System.Activator, который использует отражение, чтобы получить конструктор по умолчанию перед его вызовом».

New () компилируется в инструкцию IL "newobj", если тип известен во время компиляции. Newobj принимает конструктор для прямого вызова. Вызовы System.Activator.CreateInstance () компилируются в инструкцию IL "call" для вызова System.Activator.CreateInstance (). New () при использовании против универсальных типов приведет к вызову System.Activator.CreateInstance (). Пост г-на Ненавистника был неясен в этом вопросе ... и, ну, в общем, неприятен.

Этот код:

System.Collections.ArrayList _al = new System.Collections.ArrayList();
System.Collections.ArrayList _al2 = (System.Collections.ArrayList)System.Activator.CreateInstance(typeof(System.Collections.ArrayList));

производит этот IL:

  .locals init ([0] class [mscorlib]System.Collections.ArrayList _al,
           [1] class [mscorlib]System.Collections.ArrayList _al2)
  IL_0001:  newobj     instance void [mscorlib]System.Collections.ArrayList::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldtoken    [mscorlib]System.Collections.ArrayList
  IL_000c:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
  IL_0011:  call       object [mscorlib]System.Activator::CreateInstance(class [mscorlib]System.Type)
  IL_0016:  castclass  [mscorlib]System.Collections.ArrayList
  IL_001b:  stloc.1
5 голосов
/ 30 октября 2008

Вы не можете добавить статические методы к типу. Вы можете добавлять (псевдо) методы экземпляра только к экземпляру типа.

Смысл модификатора this состоит в том, чтобы указать компилятору C # передать экземпляр слева от . в качестве первого параметра метода static / extension.

В случае добавления статических методов к типу нет экземпляра для передачи первого параметра.

4 голосов
/ 21 ноября 2008

Я пытался сделать это с System.Environment, когда я изучал методы расширения и не увенчался успехом. Причина в том, как другие упоминают, потому что методы расширения требуют экземпляра класса.

...