2011-12-14 17 views
12
class Poly 
    { 
    public static void WriteVal(int i) { System.Console.Write("{0}\n", i); } 
    public static void WriteVal(string s) { System.Console.Write("{0}\n", s); } 
    } 

class GenWriter<T> 
    { 
     public static void Write(T x) { Poly.WriteVal(x); } 
    } 

なぜ無邪気な(C++プログラマー向け)メソッドの書き込みはC#で受け入れられないのですか?多態性、オーバーロード、ジェネリックC#

あなたはコンパイラがインスタンス化する前に、具体的な過負荷にパラメータ型Tを一致させようとしていることがわかります。

エラー3「TestGenericPolyMorph.Poly.WriteVal(int型)」の最良のオーバーロードされたメソッドが一致しているいくつかの無効な引数

もちろんです。上記のように静的メソッドを使用するのではなく、多態的な振る舞いを持つラッパーを作成することを目的としています。 注:VS 2010を使用しています。

コンパイル時に必要な情報はすべて入手できます。もう一度、問題は、検証がテンプレートのインスタンス化の前に実行されることです。議論の後

追加:

まあ、私はこれを適切にストレスがたまっていないかもしれません。 問題はジェネリックとテンプレートの違いだけでなく、さまざまな型を扱うオーバーロードのセットがあるので、これらの型の仮想メソッド(多態性)を提供するラッパークラスのセットを生成したいと考えています。 実行時の仮想メソッドの解決の価格は最小であり、パフォーマンスには影響しません。これはC++テンプレートが便利な場所です。明らかに、動的の実行時の型のオーバーヘッドは全く異なります。 したがって、既存のオーバーロードを多態性に変換し、コードを複製せずにパフォーマンスのペナルティを払うことができないかどうかが問題です(例えば、ダイナミックと比較して、構文)。

私がこれまで見てきた解決策の1つは、自動的にこれを行うためにカットアンドペーストの代わりにコードを生成/発行することでした。

C++テンプレート処理の代わりに、手動で行うか、マクロ/テンプレートプロセッサを再作成するだけです。

いいですか?

+0

"問題は、検証がテンプレートのインスタンス化の前に実行されることです。" - はい、そうです。コンパイラはコンパイル時にxの型が分からないので、C#ではこれを行うことはできません。 – TheBoyan

+1

[C#とJavaのジェネリックスとC++のテンプレートの違いは何ですか?](http://stackoverflow.com/questions/31693/what-are-the-differences-between-generics-in -c-sharp-and-java-and-templates-i) –

答えて

5

なぜあなたは、単純に書き込むことはできません。

public static void Write<T>(T x) { System.Console.Write("{0}\n", x); } 
+0

このような状況(この場合はそうでないかもしれません)では、既存の多形性があるため、x.ToString()はすべての関連する型に対して定義されています。 私は、仮想メソッドを提供するクラスで真に仮想メソッドを使わずにクラスをラップしようとしています(ただし、オーバーロードが適切に定義されています)。 –

3

C++とC#のジェネリックが異なっている(http://msdn.microsoft.com/en-us/library/c6cyy67b(v=VS.80).aspx、お好みの検索サイトで "C#のC++ジェネリック差" を検索)

ショート:C#のコンパイラは、すべての型が一致する完全なGenWriter<T>クラスを、クラス自体を見るだけで作成する必要があります。したがって、Tがint/stringまたはその他の型であるかどうかはわかりません。

C++コンパイラは、汎用クラスGenWriter<int>と宣言GenWriter<T>のインスタンスを調べて実際のクラスを作成し、その特定のインスタンスのクラスを作成します。

27

短い回答:

C#ジェネリックスはC++テンプレートではありません。類似の構文にもかかわらず、それらは全く異なっています。テンプレートはコンパイル時にインスタンス化ごとに1回作成され、テンプレート化されたコードはに対して実際にはテンプレート引数のみが提供されます。テンプレートは、インスタンス化ごとに一度、オーバーロード解決と型分析のようなタスクを行います。彼らは基本的にソースコードののスマート "検索と置換"の仕組みです。

ジェネリック型は本当にジェネリック型です。可能な型引数のいずれかがの場合は正しくなければなりません。一般的なコードは、、オーバーロード解決が行われます。などが分析されます。

長い答え:これは

What are the differences between Generics in C# and Java... and Templates in C++?

の複製では、詳細については、そこに長い答えを参照してください。誰かがGenWriter(5.0)を呼び出すことだった場合、これはGenWriter<double>(5.0)に推察されるだろう、とWrite(T x)内部メソッド呼び出しになるでしょう

http://blogs.msdn.com/b/ericlippert/archive/2009/07/30/generics-are-not-templates.aspx

+2

素晴らしい回答...いつものように:) – TheBoyan

1

public static void Write(double x) { Poly.WriteVal(x); } 

はまた、被験体に私の記事を参照してください。

WriteValの過負荷は2倍になりません。コンパイラは、WriteValの有効なオーバーロードがないことを通知しています。

C#genericsとC++のテンプレートは完全に同等ではありません。

0

コンパイラはコンパイル時にxの型がわからないため、C#ではこれを実行できません。

コンパイラは、Tの実際のタイプについて知らずにカスタムの変換を実行しようとしている可能性があると懸念しています。最も簡単な解決策はas演算子を使用することです。この演算子はカスタム変換を実行できないため、矛盾しません。

より多くのgereralソリューションはオブジェクトに最初にキャストすることです。これは、ボクシングアンボクシング問題の参考になっている:

return (int)(object) x; 

は、C#のジェネリックは、C++のテンプレートを好きでいないことを念頭に置いてもらいます。 C++テンプレートは、各タイプごとに別々にコンパイルされるコードの一部です。 C#のジェネリックはassebmlyでコンパイルされますが。