Reputation: 1394
I have an interesting situation where I'd like to use a base class utilising a type parameter to implement an interface and also keep things DRY with inheriting classes.
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
}
}
Is there something i'm missing with the generic 'where' clause or something? In my head this should work. Or does the compiler need EXACTLY the same implementation as the interface definition?
I can't easily move the type parameter into the ICalculator as there are a lot of places that it is used without any requirement for the generic.
That's cleared things up. Thanks for the info. Now obviously a solution is to make the interface take the type parameter. However ICalculator's are used in a number of places and are referenced just as ICalculator
I now get compiler errors if I omit the type parameter in Interfaces that refer to ICalculator... Is there a way to architect this that should work!?
Upvotes: 5
Views: 1326
Reputation: 660024
In my head this should work.
The problem then is in your head! :-) This should not work. Let's see why.
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);
And now there is a tiger in the giraffe paddock, and life is good for the tiger but bad for the giraffes. That's why this is illegal.
Or does the compiler need EXACTLY the same implementation as the interface definition?
The member which implements an interface member must exactly match the signature of the implemented method. For example, you cannot use return type covariance:
interface I
{
Animal GetAnimal();
}
class C : I
{
public Giraffe GetAnimal() { ... } // not legal.
}
The contract requires an animal; you provide a giraffe. That should work, logically, but this is not legal in C#. (It is in C++.)
See any of the many questions on this site about return type covariance for the reasons why.
Similarly for parameter type contravariance:
interface I
{
void PutMammal (Mammal mammal);
}
class C : I
{
public PutMammal(Animal animal) { ... } // not legal.
}
Again, this is logically sensible; the contract requires that you take a mammal, and this takes any animal. But again, this is not legal.
There are some covariant and contravariant operations in C#; see any of numerous questions on those topics on this site, or browse the covariance and contravariance articles on ericlippert.com or my previous msdn blog.
Upvotes: 8
Reputation: 101
I agree with Eric Lippert's response: you can't. And he explained in a very good way why this happens.
If you really want to do this, you can add the following to your abstract class, and it will compile:
void ICalculator.Process(ICalculationModel calcMod)
{
Process((T)calcMod);
}
But you need to know what you are doing, otherwise you might have some InvalidCastException
at runtime.
Upvotes: 0
Reputation: 101681
Yes, you need the exact implementation.
As an alternative you can make interface
and Process
method generic if it works for you:
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);
}
Upvotes: 0
Reputation: 77294
Your interface says any class implementing it will provide this method:
void Process(ICalculationModel calculationModel);
Now obviously PipeworkInspections
does not. It does not have a method Process
that accepts any ICalculationModel
. IT only has a method accepting specific implementations of ICalculationModel
. So your compilation fails.
Upvotes: 0
Reputation: 62472
If this worked then you'd be able to say something like this:
PipeworkInspections pipeworks = new PipeworkInspections();
ICalculator calculator = pipeworks;
NuclearPowerSafetyModel nuclearModel = new NuclearPowerSafetyModel();
calculator.Process(nuclearModel); // <-- Oops!
That's probably not what you wanted...
Upvotes: 0