Domanda

Sono un fan dei metodi di estensione in C #, ma non ho avuto successo con l'aggiunta di un metodo di estensione a una classe statica, come Console.

Ad esempio, se voglio aggiungere un'estensione alla console, denominata "WriteBlueLine", in modo che io possa andare:

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

Ho provato questo aggiungendo un metodo statico pubblico locale, con Console come parametro "questo" ... ma niente da fare!

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

Questo non ha aggiunto un metodo 'WriteBlueLine' alla console ... sto sbagliando? O chiedendo l'impossibile?

È stato utile?

Soluzione

No. I metodi di estensione richiedono una variabile di istanza (valore) per un oggetto. Tuttavia, è possibile scrivere un wrapper statico nell'interfaccia ConfigurationManager . Se si implementa il wrapper, non è necessario un metodo di estensione poiché è possibile aggiungere direttamente il metodo.

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

      .....

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

Altri suggerimenti

Puoi aggiungere estensioni statiche alle classi in C #? No, ma puoi farlo:

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

Ecco come funziona. Sebbene non sia possibile scrivere tecnicamente metodi di estensione statici, questo codice sfrutta una lacuna nei metodi di estensione. La scappatoia è che puoi chiamare metodi di estensione su oggetti null senza ottenere l'eccezione null (a meno che tu non acceda a qualcosa tramite @this).

Quindi ecco come lo useresti:

    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(...)

Ora PERCHÉ ho scelto di chiamare il costruttore predefinito come esempio e E perché non restituisco semplicemente T () nel primo snippet di codice senza fare tutta quella spazzatura di Expression? Bene oggi è il tuo giorno fortunato perché ottieni un 2fer. Come qualsiasi sviluppatore .NET avanzato sa, il nuovo T () è lento perché genera una chiamata a System.Activator che utilizza la riflessione per ottenere il costruttore predefinito prima di chiamarlo. Accidenti a te Microsoft! Tuttavia il mio codice chiama direttamente il costruttore predefinito dell'oggetto.

Le estensioni statiche sarebbero meglio di così, ma i tempi disperati richiedono misure disperate.

Non è possibile.

E sì, penso che la SM abbia commesso un errore qui.

La loro decisione non ha senso e costringe i programmatori a scrivere (come descritto sopra) una classe wrapper inutile.

Ecco un buon esempio: cercare di estendere la classe di test statica delle unità MS Assert: voglio un altro metodo Assert AreEqual (x1, x2) .

L'unico modo per farlo è quello di puntare a classi diverse o scrivere un wrapper attorno a centinaia di metodi Assert diversi. Perché !?

Se è stata presa la decisione di consentire estensioni di istanze, non vedo alcun motivo logico per non consentire estensioni statiche. Gli argomenti sulle librerie di sezionamento non si sollevano una volta che le istanze possono essere estese.

Mi sono imbattuto in questo thread mentre cercavo di trovare una risposta alla stessa domanda dell'OP. Non ho trovato la risposta che volevo, ma ho finito per farlo.

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

E lo uso così:

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

Forse potresti aggiungere una classe statica con il tuo spazio dei nomi personalizzato e lo stesso nome di classe:

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

No. Le definizioni del metodo di estensione richiedono un'istanza del tipo che si sta estendendo. È sfortunato; Non sono sicuro del motivo per cui è richiesto ...

Per quanto riguarda i metodi di estensione, i metodi di estensione stessi sono statici; ma vengono invocati come se fossero metodi di istanza. Poiché una classe statica non è istantanea, non si dovrebbe mai avere un'istanza della classe da cui richiamare un metodo di estensione. Per questo motivo il compilatore non consente di definire metodi di estensione per classi statiche.

Mr. Scritto odioso: " Come tutti gli sviluppatori .NET esperti sanno, il nuovo T () è lento perché genera una chiamata a System.Activator che utilizza la riflessione per ottenere il costruttore predefinito prima di chiamarlo " ;.

New () è compilato in IL "newobj" istruzione se il tipo è noto al momento della compilazione. Newobj prende un costruttore per l'invocazione diretta. Le chiamate a System.Activator.CreateInstance () vengono compilate in IL "call " istruzione per invocare System.Activator.CreateInstance (). New () se utilizzato con tipi generici comporterà una chiamata a System.Activator.CreateInstance (). Il post di Mr. Obnoxious non era chiaro su questo punto ... e beh, odioso.

Questo codice:

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

produce questo 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

Non puoi aggiungere metodi statici a un tipo. Puoi aggiungere solo metodi (pseudo-) istanza a un'istanza di un tipo.

Il punto del modificatore this è dire al compilatore C # di passare l'istanza sul lato sinistro del . come primo parametro dell'estensione statica / metodo.

Nel caso di aggiunta di metodi statici a un tipo, non vi è istanza da passare per il primo parametro.

Ho provato a farlo con System.Environment quando stavo imparando i metodi di estensione e non ci sono riuscito. Il motivo è, come altri menzionano, perché i metodi di estensione richiedono un'istanza della classe.

sì, in senso limitato.

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

Funziona ma la console no perché è statica.

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

Funziona perché fintanto che non si trova nello stesso spazio dei nomi. Il problema è che devi scrivere un metodo statico proxy per ogni metodo che ha System.Console. Non è necessariamente una cosa negativa in quanto puoi aggiungere qualcosa del genere:

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

o

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

Il modo in cui funziona è di agganciare qualcosa allo standard WriteLine. Potrebbe essere un conteggio delle righe o un filtro per parolacce o altro. Ogni volta che si specifica semplicemente Console nel proprio spazio dei nomi, si dice WebProject1 e si importa lo spazio dei nomi Sistema, WebProject1.Console verrà scelto su System.Console come predefinito per quelle classi nello spazio dei nomi WebProject1. Quindi questo codice trasformerà tutte le chiamate Console.WriteLine in blu nella misura in cui non hai mai specificato System.Console.WriteLine.

Quanto segue è stato respinto come modifica alla risposta di tvanfosson. Mi è stato chiesto di contribuire come mia risposta. Ho usato il suo suggerimento e ho completato l'implementazione di un wrapper ConfigurationManager . In linea di principio ho semplicemente compilato il ... nella risposta di tvanfosson.

  

No. I metodi di estensione richiedono un'istanza di un oggetto. Puoi   tuttavia, scrivere un wrapper statico attorno al ConfigurationManager   interfaccia. Se si implementa il wrapper, non è necessaria un'estensione   metodo poiché puoi semplicemente aggiungere direttamente il metodo.

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

Non è possibile scrivere un metodo di estensione, tuttavia è possibile imitare il comportamento richiesto.

using FooConsole = System.Console;

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

Questo ti permetterà di chiamare Console.WriteBlueLine (fooText) in altre classi. Se le altre classi vogliono accedere alle altre funzioni statiche della Console, dovranno essere esplicitamente referenziate attraverso il loro spazio dei nomi.

Puoi sempre aggiungere tutti i metodi alla classe sostitutiva se vuoi averli tutti in un unico posto.

Quindi avresti qualcosa del genere

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.
}

Ciò fornirebbe il tipo di comportamento che stai cercando.

* Nota La console dovrà essere aggiunta attraverso lo spazio dei nomi in cui è stata inserita.

Puoi usare un cast su null per farlo funzionare.

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

L'estensione:

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

YourType:

public class YourType { }

PUOI farlo se sei disposto a " frig " un po 'creando una variabile della classe statica e assegnandola a null. Tuttavia, questo metodo non sarebbe disponibile per le chiamate statiche sulla classe, quindi non sono sicuro di quanto uso sarebbe:

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();
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top