Могу ли я добавить методы расширения к существующему статическому классу?

StackOverflow https://stackoverflow.com/questions/249222

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

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

Я попробовал это, добавив локальный общедоступный статический метод с Console в качестве параметра "this"...но никаких костей!

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

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

Это было полезно?

Решение

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

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

      .....

      public static ConfigurationSection GetWidgetSection()
      {
          return GetSection( "widgets" );
      }
 }

Другие советы

Можете ли вы добавить статические расширения к классам в 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! Однако мой код вызывает конструктор объекта по умолчанию напрямую.

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

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

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

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

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

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

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

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

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

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

ConsoleColor.Cyan.WriteLine("voilà");

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

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

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

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

МистерНесносный написал:"Как знает любой продвинутый .NET разработчик, new 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

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

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

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

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

да, в ограниченном смысле этого слова.

public class DataSet : System.Data.DataSet
{
    public static void SpecialMethod() { }
}

Это работает, но консоль - нет, потому что она статична.

public static class Console
{       
    public static void WriteLine(String x)
    { System.Console.WriteLine(x); }

    public static void WriteBlueLine(String x)
    {
        System.Console.ForegroundColor = ConsoleColor.Blue;
        System.Console.Write(.x);           
    }
}

Это работает, потому что до тех пор, пока оно не находится в одном пространстве имен.Проблема в том, что вам нужно написать статический метод прокси для каждого метода, который есть в System.Консоли.Это не обязательно плохо, так как вы можете добавить что-то вроде этого:

    public static void WriteLine(String x)
    { System.Console.WriteLine(x.Replace("Fck","****")); }

или

 public static void WriteLine(String x)
    {
        System.Console.ForegroundColor = ConsoleColor.Blue;
        System.Console.WriteLine(x); 
    }

Способ, которым это работает, заключается в том, что вы подключаете что-то к стандартной строке записи.Это может быть количество строк, фильтр плохих слов или что-то еще.Всякий раз, когда вы просто указываете Console в своем пространстве имен, скажите WebProject1 и импортируйте пространство имен System, WebProject1.Консоль будет выбрана вместо System.Консоль по умолчанию для этих классов в пространстве имен WebProject1.Таким образом, этот код превратит все вызовы Console.WriteLine в синий цвет, поскольку вы никогда не указывали System.Console.WriteLine.

Следующее предложение было отклонено в качестве Редактировать на ответ тванфоссона.Меня попросили представить это в качестве моего собственного ответа.Я воспользовался его предложением и завершил реализацию ConfigurationManager обертка.В принципе, я просто заполнил ... в ответе тванфоссона.

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

public static class ConfigurationManagerWrapper
{
    public static NameValueCollection AppSettings
    {
        get { return ConfigurationManager.AppSettings; }
    }

    public static ConnectionStringSettingsCollection ConnectionStrings
    {
        get { return ConfigurationManager.ConnectionStrings; }
    }

    public static object GetSection(string sectionName)
    {
        return ConfigurationManager.GetSection(sectionName);
    }

    public static Configuration OpenExeConfiguration(string exePath)
    {
        return ConfigurationManager.OpenExeConfiguration(exePath);
    }

    public static Configuration OpenMachineConfiguration()
    {
        return ConfigurationManager.OpenMachineConfiguration();
    }

    public static Configuration OpenMappedExeConfiguration(ExeConfigurationFileMap fileMap, ConfigurationUserLevel userLevel)
    {
        return ConfigurationManager.OpenMappedExeConfiguration(fileMap, userLevel);
    }

    public static Configuration OpenMappedMachineConfiguration(ConfigurationFileMap fileMap)
    {
        return ConfigurationManager.OpenMappedMachineConfiguration(fileMap);
    }

    public static void RefreshSection(string sectionName)
    {
        ConfigurationManager.RefreshSection(sectionName);
    }
}

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

using FooConsole = System.Console;

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

Это позволит вам вызывать Console.WriteBlueLine (fooText) в других классах. Если другие классы хотят получить доступ к другим статическим функциям консоли, на них нужно будет явно ссылаться через их пространство имен.

Вы всегда можете добавить все методы в замещающий класс, если хотите, чтобы все они были в одном месте.

Так что у вас будет что-то вроде

using FooConsole = System.Console;

public static class Console
{
    public static void WriteBlueLine(string text)
    {
        FooConsole.ForegroundColor = ConsoleColor.Blue;
        FooConsole.WriteLine(text);
        FooConsole.ResetColor();
    }
    public static void WriteLine(string text)
    {
        FooConsole.WriteLine(text);
    }
...etc.
}

Это обеспечит то поведение, которое вы ищете.

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

Вы можете использовать приведение к null, чтобы заставить его работать.

public static class YoutTypeExtensionExample
{
    public static void Example()
    {
        ((YourType)null).ExtensionMethod();
    }
}

Расширение:

public static class YourTypeExtension
{
    public static void ExtensionMethod(this YourType x) { }
}

Ваш типаж:

public class YourType { }

Вы МОЖЕТЕ сделать это, если хотите "frig" немного, создав переменную статического класса и присвоив ей значение null. Однако этот метод не будет доступен для статических вызовов класса, поэтому не уверен, насколько он будет полезен:

Console myConsole = null;
myConsole.WriteBlueLine("my blue line");

public static class Helpers {
    public static void WriteBlueLine(this Console c, string text)
    {
        Console.ForegroundColor = ConsoleColor.Blue;
        Console.WriteLine(text);
        Console.ResetColor();
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top