General implementation of an interface with the specified type

I have an interesting situation where I would like to use the base class using the type parameter to implement the interface, and also keep DRY stuff with the inherited 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 that I am missing in the universal phrase "where" or something like that? In my head, this should work. Or does the compiler need EXACTLY the same implementation as the interface definition?

I cannot easily transfer the type parameter to ICalculator, as there are many places that it uses without any requirements for general.

It cleared up. Thanks for the information. Now, obviously, the solution is to force the interface to accept a type parameter. However, ICalculator is used in a number of places and is referenced as an ICalculator now I get compiler errors if I omit the type parameter in interfaces that are related to ICalculator ... Is there an architect way that this should work! <? / p>

+5
source share
5 answers

In my head, this should work.

The problem 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's a tiger in the giraffe's pen, and life is good for a tiger, but bad for giraffes. That is why it is illegal.

Or does the compiler need EXACTLY the same implementation as the interface definition?

An element that 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. This should work, logically, but it is not legal in C #. (It is in C ++.)

Look at any of the many questions on this site about return type covariance for reasons.

Similarly for the contravariance of the parameter:

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

Again, this is logically reasonable; the contract requires you to take a mammal, and that accepts any animal. But then again, this is not legal.

In C #, there are some covariant and contravariant operations; see any of the many questions on these topics on this site or browse covariance and contravariance articles on ericlippert.com or on my previous msdn blog.

+8
source

If this worked, you could say something like this:

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

This is probably not what you wanted ...

0
source

Your interface says that any class implementing it will provide this method:

 void Process(ICalculationModel calculationModel); 

Now, obviously, PipeworkInspections does not . It does not have a Process method that accepts any ICalculationModel . IT only has a method that accepts specific implementations of ICalculationModel . Thus, your compilation failed.

0
source

Yes, you need an accurate implementation.

Alternatively, you can make the interface and Process method common 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); } 
0
source

I agree with Eric Lippert's answer: you cannot. And he very well explained why this is happening.

If you really want to do this, you can add the following to your abstract class: it will be compiled:

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

But you need to know what you are doing, otherwise you may have an InvalidCastException at runtime.

0
source

Source: https://habr.com/ru/post/1243242/


All Articles