599

私はC#の拡張メソッドのファンですが、などの静的クラスに拡張メソッドを追加することに成功していませんConsole

たとえば、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これは' 'メソッドを追加しませんでしたConsole...私はそれを間違っていますか?それとも不可能なものねだり?

4

17 に答える 17

313

いいえ。拡張メソッドには、オブジェクトのインスタンス変数(値)が必要です。ただし、ConfigurationManagerインターフェイスの周りに静的ラッパーを作成することはできます。ラッパーを実装する場合は、メソッドを直接追加できるため、拡張メソッドは必要ありません。

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

      .....

      public static ConfigurationSection GetWidgetSection()
      {
          return GetSection( "widgets" );
      }
 }
于 2008-11-21T16:44:56.610 に答える
93

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

なぜ、例としてデフォルトのコンストラクターを呼び出すことを選んだのですか? また、最初のコード スニペットで new T() を返すだけで、式の無駄をすべて実行しないのはなぜですか? 今日は 2fer を取得できる幸運な日です。高度な .NET 開発者なら誰でも知っているように、new T() は、呼び出す前にリフレクションを使用して既定のコンストラクターを取得する System.Activator への呼び出しを生成するため、低速です。くそーマイクロソフト!ただし、私のコードはオブジェクトのデフォルトのコンストラクターを直接呼び出します。

静的拡張はこれよりも優れていますが、絶望的な時代には絶望的な手段が必要です。

于 2011-03-27T19:20:53.930 に答える
63

不可能です。

はい、MS はここで間違いを犯したと思います。

彼らの決定は意味がなく、プログラマーは (前述のように) 無意味なラッパー クラスを作成する必要があります。

良い例を次に示します: 静的 MS ユニット テスト クラスを拡張しようとしています Assert: I want 1 more Assert method AreEqual(x1,x2).

これを行う唯一の方法は、異なるクラスを指すか、何百もの異なる Assert メソッドのラッパーを作成することです。どうして!?

インスタンスの拡張を許可するという決定が下された場合、静的拡張を許可しない論理的な理由はわかりません。インスタンスを拡張できるようになると、ライブラリのセクション化に関する議論は成立しません。

于 2009-01-12T14:44:24.980 に答える
21

たぶん、カスタム名前空間と同じクラス名で静的クラスを追加できます。

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);
        }
    }
}
于 2010-01-07T04:11:31.477 に答える
17

いいえ。拡張メソッドの定義には、拡張するタイプのインスタンスが必要です。残念です。なぜそれが必要なのかわかりません...

于 2008-11-21T16:44:39.793 に答える
7

型に静的メソッドを追加することはできません。型のインスタンスには (疑似) インスタンス メソッドのみを追加できます。

修飾子のポイントは、静的/拡張メソッドの最初のパラメーターとしてのthis左側のインスタンスを渡すように C# コンパイラに指示することです。.

型に静的メソッドを追加する場合、最初のパラメーターに渡すインスタンスはありません。

于 2008-10-30T04:00:49.090 に答える
7

拡張メソッドに関しては、拡張メソッド自体は静的です。ただし、それらはインスタンス メソッドであるかのように呼び出されます。静的クラスはインスタンス化できないため、クラスのインスタンスから拡張メソッドを呼び出すことはできません。このため、コンパイラは拡張メソッドを静的クラスに定義することを許可していません。

Obnoxious 氏は、次のように書いています。

コンパイル時にタイプがわかっている場合、New() は IL の「newobj」命令にコンパイルされます。Newobj は、直接呼び出し用のコンストラクターを取ります。System.Activator.CreateInstance() への呼び出しは、System.Activator.CreateInstance() を呼び出す IL "call" 命令にコンパイルされます。New() をジェネリック型に対して使用すると、System.Activator.CreateInstance() が呼び出されます。Obnoxious 氏による投稿は、この点について不明確でした...そして、まあ、不愉快です。

このコード:

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
于 2012-01-11T19:35:41.130 に答える
4

拡張メソッドを学習していたときに System.Environment でこれを実行しようとしましたが、成功しませんでした。その理由は、他の人が言及しているように、拡張メソッドにはクラスのインスタンスが必要だからです。

于 2008-11-21T16:39:23.440 に答える
1

はい、限られた意味で。

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.Console が持つすべてのメソッドに対してプロキシの静的メソッドを作成する必要があることです。次のようなものを追加できるため、必ずしも悪いことではありません。

    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 などの名前空間で Console を指定し、名前空間 System をインポートすると、WebProject1.Console が名前空間 WebProject1 のこれらのクラスのデフォルトとして System.Console よりも選択されます。したがって、このコードは、System.Console.WriteLine を指定していない限り、すべての Console.WriteLine 呼び出しを青に変えます。

于 2014-11-09T17:41:56.237 に答える
1

以下は、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);
    }
}
于 2015-11-21T11:57:59.897 に答える
0

Although the methods of Console are static, its static methods Write() and WriteLine() merely redirect the call to Console.Out.Write() and Console.Out.WriteLine() respectively. Out is an instance whose type derives from the abstract class TextWriter. This makes it possible to define extension methods for TextWriter:

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

    public static void WriteUppercase(this TextWriter writer, string text)
    {
        writer.Write(text.ToUpper());
    }
}

The method can then be invoked like this:

Console.Out.WriteBlueLine();

And the best part is that the type of the standard error stream instance Console.Error also derives from TextWriter which makes the same extension method also usable for Console.Error:

Console.Error.WriteBlueLine();

This can be quite useful if you have defined an extension method like WriteTable()(for writing a table out to the console) because you can also use it for the error stream or any other object of TextWriter.

Newer versions of C# allow this to be even shorter with a using static statement for Console to get red of the Console. prefix:

using static System.Console;

Out.WriteBlueLine("A blue line");
Error.WriteBlueLine("A blue line");
于 2022-01-29T09:18:32.427 に答える
-5

静的クラスの変数を作成し、それを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();
    }
}
于 2010-09-15T14:39:26.063 に答える