web-dev-qa-db-ja.com

C#ジェネリックがC ++テンプレートのようにジェネリック型パラメーターの1つから派生できないのはなぜですか?

C#ジェネリックがC++テンプレートのようにジェネリック型パラメーターの1つから派生できないのはなぜですか? CLRがこれをサポートしていないので不可能だとわかっているのですが、なぜですか?

C++テンプレートとC#ジェネリックの大きな違いを認識しています。前者はコンパイル時のエンティティであり、コンパイル中に解決する必要がありますが、後者はファーストクラスのランタイムエンティティです。

それでも、理由 CLR設計者が、CLRジェネリック型がそのジェネリック型パラメーターの1つから派生できるようにするスキームを思い付かなかった理由がわかりません。結局のところ、これは非常に便利な機能であり、私は個人的にそれを大いに見逃しています。

編集:

まだ実装されていないことを正当化するこの機能の実装に非常に高い価格をもたらす修正という、ハードコアの問題について知りたいと思います。たとえば、次の架空の宣言を調べてください。

class C<T> : T
{
}

Eric Lippertが気付いたように、「Tが構造体の場合、Tがシールされたクラス型の場合、Tがインターフェイス型の場合、TがCの場合、Tが派生クラスの場合、 Cから?Tが抽象メソッドを持つ抽象型の場合はどうなりますか?TのアクセシビリティがCよりも低い場合はどうなりますか?TがSystem.ValueTypeの場合はどうなりますか?(System.ValueTypeから継承する非構造体を使用できますか?) System.Delegate、System.Enumなど? "

エリックが続けているように、 "これらは簡単で明白なものです"。確かに、彼は正しいです。簡単でも明白でもない問題の具体例に興味がありますが、それは解決が困難です。

43
mark

さて、class C<T> : T { }で何がうまくいかない可能性があるかを自問することから始めましょう。非常に多くのことがすぐに思い浮かびます。

Tが構造体の場合はどうなりますか? Tが封印されたクラスタイプの場合はどうなりますか? Tがインターフェイスタイプの場合はどうなりますか? TがC<T>の場合はどうなりますか?! TがC<T>から派生したクラスである場合はどうなりますか? Tが抽象メソッドを持つ抽象型である場合はどうなりますか? TのアクセシビリティがCよりも低い場合はどうなりますか? TがSystem.ValueTypeの場合はどうなりますか? (System.ValueTypeから継承する非構造体を使用できますか?)System.Delegate、System.Enumなどはどうですか?

それらは簡単で明白なものです。提案された機能は、タイプとその基本タイプの間の相互作用について、文字通り何千とは言わないまでも、数百の微妙な質問を開きます。これらはすべて、注意深く指定、実装、およびテストする必要があります。私たちは間違いなくいくつかを見逃し、それによって将来的に重大な変更を引き起こしたり、実装定義の動作でランタイムをサドルしたりします。

費用は莫大になるので、利益は莫大な方がよいでしょう。ここでは大きなメリットは見られません。

62
Eric Lippert

さて、私の前の答えが気に入らなかった場合は、別の方法を取りましょう。

あなたの質問は虚偽を前提としています:理由からない機能を実装する必要があるということです。それどころか、機能を実装するには、非常に正当な理由が必要です。機能は、初期費用、保守費用、および機会費用において非常に高価です。 (つまり、機能Xに費やす時間は、機能Yに費やすことができない時間であり、機能Zを実行できなくなる可能性があります。)お客様や利害関係者に責任を持って価値を提供するために、すべての機能を実装することはできません。誰かがたまたま好きだと。

特に素晴らしいと思う機能を実装しなかった理由を正当化するのは、ランタイム設計者の責任ではありません。機能は、コストとユーザーへのメリットに基づいて優先順位が付けられており、ユーザーはこの種の継承を要求して私のドアを正確に打ち負かしていません。この特定の機能は、型システムの分析がランタイムでどのように機能するかを大幅に変更し、ジェネリックスを消費するすべての言語に広範囲にわたる影響を及ぼし、ほとんどメリットがないように思われます。

この種の継承は、C++で記述されたコンパイラで使用されます。結果のコードは、追跡が難しく、保守が難しく、デバッグが混乱します。私はこのようなコードを徐々に排除するために最善を尽くしてきました。非常に説得力のあるメリットがない限り、C#で同じ種類の悪いパターンを有効にすることには反対です。

その莫大な利益を説得力のある方法で説明するタスクは、機能を実装する必要がある人々ではなく、機能を必要とする人々に課せられます。では、説得力のあるメリットは何ですか?

34
Eric Lippert

これが役立つ可能性のあるコードの例:

public class SpecialDataRow<T> : T where T : DataRow
{
    public int SpecialFactor { get; set; }
}

これにより、DataRowおよび派生DataRow(型付きデータセットで生成されたものなど)から「特別な」行を作成できるようになります。

そのようなクラスをコーディングする方法は他にありません

13
maliger

これについて何がそんなに役立つでしょうか?

名前にもかかわらず、ジェネリックはジェネリックプログラミングをサポートすることを意図したものではなかったことを忘れないでください。

このような機能をサポートするには、CLRにかなり劇的な変更を加える必要があります。

コンパイル時にも存在しない型から派生するクラスを定義する必要があります。

なぜ彼らはそのようなフープを飛び越えて、この機能を追加するためだけに型システムをかなり根本的に妥協する必要があるのでしょうか?その価値はありますか?

そう思うなら、その理由を教えてください。 connect.Microsoft.comにフィードバックを書いて、この機能が非常に基本的で追加する必要がある理由を伝えます。

2
jalf

C++テンプレートをC#ジェネリックと比較することはできません。 C++テンプレートはマクロのように前処理されますが、.NETのジェネリックはランタイムによって処理されます。

しかし、それについて私よりも多くのことを知っている他の人々がいます...

1