2016-12-23 10 views
3

私はC++でテンプレートを使用しましたが、これまでC#のジェネリックであまり気にしないようにしました。ここで私は何をしようとしているの単純化されたカットダウン版では(これはC++で可能になります)です:C#ジェネリッククラスの呼び出しメソッドはプリミティブでオーバーロードされます

class DoesStuffWithPrimatives 
{ 
    public void DoStuff(double value) { } 
    public void DoStuff(string value) { } 
    public void DoStuff(int value) { } 
    public void DoStuff(uint value) { } 
    // etc... 
} 

class GenericBase<T> 
{ 
    private readonly T _testValue; 
    private DoesStuffWithPrimatives _doesStuff = new DoesStuffWithPrimatives(); 
    public GenericBase(T testValue) 
    { 
     _testValue = testValue; 
    } 

    public void DoStuff() 
    { 
     _doesStuff.DoStuff(_testValue); 
    } 
} 

class DoubleContrete : GenericBase<double> 
{ 
    public DoubleContrete() : base(1.54545487) 
    { 
    } 
} 

class IntConrete : GenericBase<int> 
{ 
    public IntConrete() : base(80085) 
    { 
    } 
} 

私は(GenericBase<T>DoStuff()方法で)以下のコンパイルエラーが表示されます。

エラーCS1503引数1:「ダブル」

なぜDoesStuffWithPrimatives.DoStuff(…)オーバーロードの呼び出すためのコンパイラ解決できないに「T」から変換することはできません!

答えて

1

ジェネリックでは、コンパイラは、型パラメータ(この場合はT)は、指定したものの基数を持つ任意の型にすることができます。ベースを指定しなかったので、コンパイラはTを、文字通り何でもあるObjectから継承するものとして扱います。

コンパイラは、それがdoubleではないことをTの種類ごとに意味がありませんので、あなたのTがダブルであると判断することはできません。あなただけTためdoubleを置き換えることができるよう、Tが二重であるとき、これは正常に動作します

public void DoStuff<T>(T param) 
{ 
    DoStuffWithDouble(param); 
} 

public void DoStuff(double param) 
{ 
    DoStuffWithDouble(param); // param is a double, so no problem 
} 

しかし、Tが何かすることができ、例えば、以下の一般的な方法を取りますそうでなければ、Listのように。この場合、このコードはコンパイルされません。

public void DoStuff(List param) 
{ 
    DoStuffWithDouble(param); // param is not double, this wouldn't compile 
} 

コンパイラはそうすることがTが倍増されていないことをどこでも壊れるのでTは、二重であるという仮定をすることはできません。

もちろん、キャストしたり、オブジェクトの型チェックを実行することもできます。

public void DoStuff<T>(T param) 
{ 
    if (param is double) 
     // Only runs if T is confirmed to be a double, so no chance for errors 
     DoStuffWithDouble((double)param); 
} 
+0

を使用してCreateDelegateを処理する必要があります。あなたがその型のメソッドを使うことを可能にするために、ジェネリッククラスの型パラメータを簡単に使用するといいでしょう。 –

+0

また、 'DoStuff(オブジェクト値)'を追加すると面白いことになりますが、エラーは起こりますが、typeパラメータで指定した内容にかかわらず、コードは 'DoStuff(オブジェクト値)'を使用するために焼き付けられます。 –

+1

'T'を' double'にキャストできません。私はコードを試してコンパイルに失敗します。 – Sweeper

0

コードが動作しない理由はTは種類の固定セットに限定されないことです。 、_testValueは何でも、IEnumerableButtonことができるので、当然の

public void DoStuff() 
{ 
    _doesStuff.DoStuff(_testValue); 
} 

それコンパイルされません:構造体、クラス、デリゲート、インターフェイス...

あなたがこれを行う:Tいかなるタイプが可能ですXmlSerializerDictionary<int, int>CultureInfo、またはあなたが作成したいくつかのタイプでさえ、極端なものの名前をいくつか挙げるに過ぎません。明らかに、CultureInfoを、doubleまたはintを受け入れるメソッドに渡すことはできません。

エラーメッセージは、ちょうどTというコンパイラの方法であり、任意のタイプでもかまいません。doubleまたはintではありません。

この問題の解決策の1つは単純ですが、ジェネリックはまったく使用しないでください!私の意見では、私はあなたがこのシチュエーションでジェネリック医薬品を使用するべきではないと思います。

ジェネリックは、使用しているタイプについてほとんど気にしない(または気にしない)ときに使用してください。たとえば、List<T>は、格納するタイプを気にしません。どんなタイプでも動作します。他のいくつかのクラス/インタフェース/メソッドは、特定の属性を持つ型で動作します:特定のインタフェースを実装し、デフォルトのコンストラクタを持ち、クラスです。それで、ジェネリック型の制約があります。しかし、これらの制約を満たす型は常に無限に存在します。特定のインターフェイスを実装する型またはデフォルトのコンストラクタを持つ型をいつでも作成できます。ジェネリック型を有限個の型に制約するジェネリック制約は存在しません。これは、限り「プリミティブ」のようなものです。

0

前述のとおり、Tがいずれのオーバーロードに対しても有効なタイプであることはコンパイラが分かりません。しかし、適切な方法を得るためにリフレクションを使用することは可能です。適切な処理をしなければ、これはもちろんランタイムエラーの原因となる可能性があり、オーバーヘッドがあります。オーバーヘッドは、静的変数を持つことが可能な場合は、ほとんど無効にすることができます。 Action<T>「dostuffer」以下の例では、それが使用されるとき、各タイプに対して一度決定されるであろう。

class GenericBase<T> 
{ 
    private readonly T _testValue; 

    public GenericBase(T testValue) 
    { 
     _testValue = testValue; 
    } 


    static readonly DoesStuffWithPrimatives _doesStuff = new DoesStuffWithPrimatives(); 
    static readonly Action<T> dostuffer = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>),_doesStuff, 
     typeof(DoesStuffWithPrimatives).GetMethod(nameof(DoesStuffWithPrimatives.DoStuff), new[]{typeof(T)})); 


    public void DoStuff() 
    { 
     dostuffer(_testValue); 
    } 
} 

(それはいくつかの異なる変数からなることができる場合)DoesStuffWithPrimativesクラスが静的でない場合は、静的変数はメソッド情報であり、インスタンスコンストラクタGenericBaseは、DoesStuffWithPrimatives変数

関連する問題