2016-02-17 22 views
5

インターフェイスを実装するために型パラメータを使用する基本クラスを使用し、クラスを継承してDRYを維持するという興味深い状況があります。指定された型のインターフェイスの汎用実装

public interface ICalculator 
{ 
    void Process(ICalculationModel calculationModel); 
} 

public abstract class CalculatorBase<T> :ICalculator where T : ICalculationModel 
{ 
    // Compiler moans that Process(ICalculationModel calculationModel) isn't implemented 
    public abstract void Process(T calculationModel); 
} 

public class PipeworkInspections : CalculatorBase<GasSafetyModel> 
{ 
    public override void Process(GasSafetyModel documentModel){ 
     //snip 
    } 
} 

ここでは、どこの句で何かが分からないのですか?私の頭の中でこれはうまくいくはずです。あるいは、コンパイラはインタフェース定義と同じ実装を正確に必要としますか?

タイプパラメータをICalculatorに簡単に移動することはできません。場所の数が多いため、ジェネリックの要件がなくても使用されます。

これで解決しました。情報をありがとう。今や明らかに解決策は、インタフェースが型パラメータを取るようにすることです。しかし、ICalculatorはいくつかの場所で使用されていて、ちょうどICalculatorとして参照されています。ICalculatorを参照するインタフェースの型パラメータを省略すると、コンパイラエラーが発生します。

+0

「ICalculator」での表示方法と同じである必要があります。しかし、私は主にJavaのものでC#ではなく、私は確かにそれを言うことはできません。 – Powerlord

+0

これはあなたが 'ICalculator c = new PipeworkInspections();を実行することができたので機能しません。 c.Process(新しいOtherModel()) 'は安全ではありません。あなたができることは、 'ICalculator.Process'を暗示し、汎用実装の中にキャストすることです。 – Lee

答えて

0

これが働いていたなら、あなたはこのような何か言うことができるだろう:これは動作するはずです...あなたは何を望むか、おそらく私の頭の中で

8

ではありません

PipeworkInspections pipeworks = new PipeworkInspections(); 
ICalculator calculator = pipeworks; 

NuclearPowerSafetyModel nuclearModel = new NuclearPowerSafetyModel(); 
calculator.Process(nuclearModel); // <-- Oops! 

を。

問題はあなたの頭の中にあります! :-)これは動作しません。なぜか見てみましょう。

interface ICage 
{ 
    void Enclose(Animal animal); 
} 
class ZooCage<T> : ICage where T : Animal 
{ 
    public void Enclose(T t) { ... } 
} 
... 

var giraffePaddock = new ZooCage<Giraffe>(); 
var cage = (ICage)giraffePaddock; 
var tiger = new Tiger(); 
icage.Enclose(tiger); 

そして今、そこに虎がキリンのパドックであり、そして人生は虎のために良いが、キリンに悪いです。だから、これは違法です。

コンパイラはインターフェイス定義と同じ実装を正確に必要としますか?

インターフェイスメンバを実装するメンバは、実装されたメソッドのシグネチャと完全に一致する必要があります。たとえば、戻り型共分散は使用できません。

interface I 
{ 
    Animal GetAnimal(); 
} 
class C : I 
{ 
    public Giraffe GetAnimal() { ... } // not legal. 
} 

この契約では動物が必要です。あなたはキリンを提供します。論理的にはうまくいくはずですが、これはC#では合法ではありません。 (これはC++で書かれています)

理由の理由から、戻り値の共分散に関するこのサイトの多くの質問を参照してください。パラメータタイプcontravarianceについても同様

interface I 
{ 
    void PutMammal (Mammal mammal); 
} 
class C : I 
{ 
    public PutMammal(Animal animal) { ... } // not legal. 
} 

は再び、これは論理的に賢明です。契約では哺乳動物を服用する必要があり、これには動物が必要です。しかし、これもまた合法ではありません。

C#にはいくつかの共変および反変の操作がありますが、このサイトのこれらのトピックに関する多数の質問を参照するか、ericlippert.comまたは以前のmsdnブログの共分散および反差異に関する記事を参照してください。

void Process(ICalculationModel calculationModel); 

が今明らかにPipeworkInspectionsないを行います。

+0

インターフェイスの実装(または士気相当物)がメソッドの引数に関して反反的であるようにする任意の言語を知っていますか?私は戻り値の型共分散(あなたはC++に言及したばかりです)をいくつか聞いたことがありますが、パラメータの反動はありません。 – Servy

+0

@サービ:エッフェルはおそらく?私はエッフェル塔がその特徴を持っていることを聞いているようです。私はエッフェルを一度も使用していないので、私は確かに言うことができません。 C#では、メソッドをデリゲートに変換する際にパラメータの反動が許されますが、これは多分私が想定している道徳上の同等物です。デリゲートをInvokeという単一のメソッドを持つインターフェイスとして考えることができます。 –

+0

@セイビー:私はそれを探しました。エッフェルは* unsafe * parameter * covariance *をサポートしています。 SatherはEiffelのような言語で、パラメータの反動をサポートしています。 –

0

あなたのインターフェイスは、それを実装する任意のクラスは、この方法を提供する予定だという。 ICalculationModelを受け入れる方法Processはありません。 ITには、ICalculationModelという特定の実装を受け入れる方法があります()。コンパイルに失敗します。

0

はい、正確な実装が必要です。それはあなたのために働く場合は、interfaceProcess方法ジェネリックにすることができ、代替として

public interface ICalculator<T> where T : ICalculationModel 
{ 
    void Process(T calculationModel); 
} 

public abstract class CalculatorBase<T> : ICalculator where T : ICalculationModel 
{ 
    public abstract void Process(T calculationModel); 
} 
0

私はエリックリペットの応答に同意する:あなたがすることはできません。そして、彼は非常に良い方法でこれがなぜ起こるのか説明しました。

あなた本当にはあなたの抽象クラスに以下を追加することができ、それがコンパイルされ、これをしたい場合:特にあなたが持っているかもしれませんが、

void ICalculator.Process(ICalculationModel calcMod) 
{ 
    Process((T)calcMod); 
} 

をしかし、あなたは何をしているかを知る必要があります実行時にはInvalidCastExceptionがあります。

関連する問題