web-dev-qa-db-ja.com

C#静的クラス拡張メソッドがサポートされていないのはなぜですか?

この質問 から、拡張メソッドはクラスインスタンスでのみ操作でき、静的クラス自体では操作できないことがわかります。これは、ConvertMathのような便利な静的クラスを拡張できないことを意味します。

私が知りたいのは、なぜこれが当てはまるのかということです。上記のリンクから、C#チームがこの種の機能を実装する方法についていくつかの提案があります。それがサポートされていないという哲学的な理由はありますか?

たとえば、ForEach<T>の組み込みLINQIEnumerable<T>拡張機能がない理由の背後にある rationale です。

56
Justin Morgan

c#チームは、この種の機能を実装できたはずです。それがサポートされていないという哲学的な理由はありますか?

技術的な理由も哲学的な理由もありません。ただし、よく指摘するように、notが機能を実行する理由を説明する必要はありません。機能は安くはありません。それらは非常に高価であり、彼ら自身のコストを正当化するだけでなく、私たちできた他の100の機能を実行しない機会費用を正当化する必要がありますその予算。機能のコストを利害関係者に正当化する必要がありますが、基準を満たさない機能をnot実装することによって時間と労力を節約することを正当化する必要はありません。

特に、提案された機能はLINQには何もしません。 LINQを機能させるために、拡張メソッドが追加されました。 LINQを機能させなかったものはすべて、C#3.0に組み込むのが非常に困難でした。スケジュールに多くの作業があり、作業に時間がかかりませんでした。(自動プロパティが組み込まれたことに驚きました。)設計する前に不要な機能をカットすることで、他の作業に費やす時間と労力を大幅に節約できました。 doがLINQを機能させるもの。

つまり、提案された機能は、コストに対する純利益の基準を満たしたことがなく、限られた時間と労力を費やすためのより重要な機能が常にありました。

72
Eric Lippert

回答といくつかの関連する質問を読んだ後、私はここで問題の理解をまとめました。

拡張メソッドのしくみ

まず、静的メソッドの場合、 拡張子は単なる構文糖衣構文です であることを理解することが重要です。

// Say you have an extension method that looks like this:
class Extensions
{
    public static void Extend(this SomeClass foo) {}
}

// Here's how you call it
SomeClass myClass;
myClass.Extend();

// The compiler converts it to this:
Extensions.Extend(myClass);

メソッドは実際にはクラスの一部にはなりません。これが、拡張メソッドから プライベートメンバーにアクセスできない である理由です。拡張メソッドはC#構文のみを変更し、OOPアクセシビリティの概念に違反しません。実際、同じことを行う拡張メソッドと通常の静的メソッドを作成する場合は、MSILを逆コンパイルします。 、それらは まったく同じ です。

拡張メソッドが存在する理由

では、実際の機能が追加されていないのに、なぜ拡張メソッドがあるのでしょうか。答えはLINQです:

// LINQ makes this easy to read
array.Where(i => i&1 == 0).Select(i => i*i);

// Without extension methods, we would have to do it like this
Enumerable.Select(Enumerable.Where(array, i => i&1 == 0), i => i*i);

ある意味で、LINQのすべては単なる構文糖衣です。なぜなら、それができることはすべて、不格好で非LINQyの方法で記述できるからです。明らかに、C#チームは、LINQによって得られた読みやすさはそれだけの価値があると感じていましたが、「なぜそこで止まったのか」という疑問を投げかけています。

なぜ他の拡張タイプではないのですか?

C#コンパイラ開発者の1人であるEric Lippertは、 ブログ投稿 で、C#3の大部分がLINQに必要なすべての構造を作成していると説明しています: "暗黙的に型付きローカル、匿名型、ラムダ式、拡張メソッド、オブジェクトとコレクションの初期化子、クエリ内包表記、式ツリー、[および]メソッド型推論の改善。 "C#チームが最もリソースが多かったため- 2008 .NETリリースの限定チームでは、LINQに厳密に必要ではない追加の種類の拡張機能は含まれていませんでした。

チームはC#4で拡張プロパティを実装することを検討し、実際に機能するプロトタイプを作成しましたが、実装されたWPFチーム(機能の動機の1つ)が有効にならないことがわかったため、削除されました。 Eric Lipperは後で、静的クラスの拡張メソッド 検討しました と述べましたが、実装、テスト、および保守のコストに対する実際の利点を正当化することはできませんでした。

回避策

近づく拡張メソッドを書くことは可能です:

public static TResult DoSomething<TType, TResult>(this TType @class)
    {
        // access static methods with System.Reflection
        return default(TResult);
    }

// This works, but poorly
typeof(Math).DoSomething();
typeof(Convert).DoSomething();

しかし、それはかなり醜いです。リフレクションが必要であり、Typeが呼び出すことができ、意図した機能ではない可能性があるため、インテリジェントなタイピングをサポートすることはできません。

12
Justin Morgan

拡張メソッドは、クラスではなくオブジェクトを操作します。拡張メソッドをクラスで操作したい場合は、次のように実行できると思います。

public static T DoSomething<T>(this T @class) 
    where T:Type
{
    // access static methods via reflection...
    return @class;
}
3
kelloti

あなたの質問に対する答えはbecause it doesn't make any sense to extend with static methodsだと思います。 Extension methodsを導入する主な理由は、所有していないextendingクラスではありません。主な理由は、次のような例でネストするメソッドを減らすことができるためです。

 Enumerable.Select(Enumerable.Where(arr, i => i & 1 == 0), i => i*i); // not the best thing I ever read

 arr.Where(i => i&1 == 0).Select(i => i*i); // wow, I see! These are squares for odd numbers

そのため、静的クラスにextensionメソッドを使用する必要はありません。

3
Snowbear

F#では静的拡張が可能です:

type Platform = 
    | Win32
    | X64
    override this.ToString() = 
        match FSharpValue.GetUnionFields(this, typeof<Platform>) with
        | case, _ -> case.Name.Replace('X', 'x')

type Environment with
    static member Platform =
        if System.IntPtr.Size = 8 then Platform.X64 else Platform.Win32
2
ded'

それは実装されていないだけでなく、メソッドがどこに属しているのか混乱を引き起こしますか?静的拡張機能が導入されたのは、linqが新しいバージョンで提供され、コード化が容易な方法でlinqをサポートするためだけに、静的拡張機能が役立つためです。

静的拡張は、コードを読みやすくするためにのみ役立ちます。実行時またはコンパイル時には意味がありません。

1
Akash Kava