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



Я поклонник методов расширения в 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' в консоль... я делаю это неправильно? Или прося невозможного?

641   15  

15 ответов:

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

вот как это работает. Хотя вы не можете технически написать статические методы расширения, вместо этого этот код использует лазейку в методах расширения. Эта лазейка заключается в том, что вы можете вызывать методы расширения для объектов null без получения исключения null (если вы не получаете доступ к чему-либо через @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(...)

теперь почему я выбрал вызов конструктора по умолчанию в качестве примера, и почему бы мне просто не вернуть новый T () в первом фрагменте кода без выполнения всего этого мусора выражения? Хорошо сегодня ваш счастливый день, потому что вы получаете 2fer. Как известно любому продвинутому разработчику .NET, новый T () медленный, потому что он генерирует вызов системы.Активатор, который использует отражение для получения конструктора по умолчанию перед его вызовом. Черт бы тебя побрал, Microsoft! Однако мой код вызывает конструктор по умолчанию для объекта непосредственно.

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

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

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

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

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

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

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

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

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

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

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

и я использую его так:

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

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

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

New () компилируется в инструкцию IL "newobj", если тип известен во время компиляции. Newobj принимает конструктор для прямого вызова. Звонки в систему.Активатор.CreateInstance () компилируется в инструкцию IL "call" для вызова системы.Активатор.CreateInstance (). Новый () при использовании против универсальных типов приведет к вызову системы.Активатор.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.

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

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

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

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

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

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

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

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

нет. Методы расширения требуют экземпляр объекта. Вы можете однако напишите статическую оболочку вокруг 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);
    }
}

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

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

расширения:

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

YourType:

public class YourType { }

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

using FooConsole = System.Console;

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

Это позволит вам вызвать консоль.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. Однако этот метод не будет доступен для статических вызовов класса, поэтому не уверен, насколько он будет использоваться:

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

Comments

    Ничего не найдено.