Inheritance problem in C#
I'm refactoring some code and want to classes a bit higher in the inheritance chain be a bit more strict with their parameters. As I'm not sure I'm explaining this correctly, here's what I've got:
public interface ISvdPredictor
{
List<string> Users { get; set; }
List<string> Artists { get; set; }
float PredictRating(ISvdModel model, string user, string artist);
float PredictRating(ISvdModel model, int userIndex, i开发者_运维知识库nt artistIndex);
}
ISvdPredictor
uses ISvdModel
:
public interface ISvdModel
{
float[,] UserFeatures { get; set; }
float[,] ArtistFeatures { get; set; }
}
Now I want to implement another variation:
public interface IBiasSvdPredictor : ISvdPredictor
{
float PredictRating(IBiasSvdModel model, string user, string artist);
float PredictRating(IBiasSvdModel model, int userIndex, int artistIndex);
}
Which uses IBiasSvdModel
which derives from ISvdModel
:
public interface IBiasSvdModel : ISvdModel
{
float GlobalAverage { get; set; }
float[] UserBias { get; set; }
float[] ArtistBias { get; set; }
}
IBiasSvdPredictor
will not work with ISvdModel
.
The problem is that when I implement IBiasSvdPredictor
I'd have to implement 2 pairs of PredictRating methods. One from ISvdPredictor
and the other from IBiasSvdPredictor
. What do I need to do to be able to just implement those from IBiasSvdPredictor
?
I've tried generics as well, but couldn't restrict the PredictRating
for BiasSvdPredictor
to IBiasSvdModel
using the where
directive. I may be doing this all wrong so any suggestion might help. I think you get what I'm trying to do.
EDIT: If anyone needs more context see https://github.com/gligoran/RecommendationSystem. I'm writing this code for my thesis for BSc.
You could use generics and constraints.
public interface ISvdModel
{
float[,] UserFeatures { get; set; }
float[,] ArtistFeatures { get; set; }
}
public interface IBiasSvdModel : ISvdModel
{
float GlobalAverage { get; set; }
float[] UserBias { get; set; }
float[] ArtistBias { get; set; }
}
public interface ISvdPredictor<in TSvdModel>
where TSvdModel : ISvdModel // Require that TSvdModel implements ISvdModel
{
List<string> Users { get; set; }
List<string> Artists { get; set; }
float PredictRating(TSvdModel model, string user, string artist);
float PredictRating(TSvdModel model, int userIndex, int artistIndex);
}
// I would actually avoid declaring this interface. Rather, see comment on the class.
public interface IBiasSvdPredictor : ISvdPredictor<IBiasSvdModel> { }
class BiasSvdPredictor : IBiasSvdPredictor // Preferred : ISvdPredictor<IBiasSvdModel>
{
// ...
public float PredictRating(IBiasSvdModel model, string user, string artist) { }
public float PredictRating(IBiasSvdModel model, int userIndex, int artistIndex) { }
}
The interface
should have one method, PredictRating. I wouldn't have two interfaces that have the same method to implement. Confusing.
Create an abstract
class that implements your interface
. Make PredictRating a virtual
method so inheritors can override as they see fit. You could even do a default implementation on the abstract class.
One interface, One abstract class. N concrete class that implement PredictRating as they see fit.
public interface Demo
{
int PredictRating(int param1);
}
public abstract class AbstractDemo : Demo
{
public virtual int PredictRating(int param1)
{
return param1 + 1;
}
}
public class ClassDemo1 : AbstractDemo
{
//This guy uses AbstractDemo Predict Rating
public override int PredictRating(int param1)
{
return base.PredictRating(param1);
}
}
public class ClassDemo2 : AbstractDemo
{
//This guy overrides the predict rating behavior
public override int PredictRating(int param1)
{
return param1 + 2;
}
}
You have to implement all four methods. They have different signatures and thus are considered to be different. However, you can have one delegate to the other, and sometimes using explicit implementation helps with that.
public class Foo : IBiasSvdPredictor {
public float PredictRating(IBiasSvdModel, string user, string artist) { .... }
// this is an expicit implementation of ISvdPredictor's method. You satisfy
// the interface, but this method is not a public part of the class. You have to
// cast the object to ISvdPredictor in order to use this method.
float ISvdPredictor.PredictRating(ISvdModel model, string user, string artist) {
this.PredictRating((IBiasSvdModel)model, user, artist);
}
}
This of course will not work if the ISvdModel is not actually an IBiasSvdModel.
You can use explicit interface implementation to hide the ones from ISvdPredictor
, but you should implement them all or have a base abstract class to handle them.
I'd have to implement 2 pairs of PredictRating methods.
Of course you do. What did you expect?
If your IBiasSvdPredictor
must take a IBiasSvdModel
in its PredictRating
method, than IBiasSvdPredictor
is not an ISvdPredictor
(because it cannot take a ISvdModel
as the first parameter to PredictRating
) and inheriting IBiasSvdPredictor
from ISvdPredictor
is the wrong choice.
In my opinion, you should simply keep the interfaces separate and not inherit one from the other.
Without having a full understanding of your object model (so this may not actually apply in your situation), it seems like maybe ISvdModel
shouldn't be part of the interface definition. It seems more like it's an implementation detail, not necessarily part of the contract you're trying to enforce. To me it makes more sense to pass ISvdModel
(or IBiasSvdModel
) into the constructor of your implementation class, not have it as part of your ISvdPredictor
interface. Then you wouldn't need 2 separate interface definitions at all, you would just have 2 implementations of the single interface.
You might even be able to take it one step further; if the only difference between ISvdPredictor
and IBiasSvdPredictor
is that one uses a ISvdModel
and the other uses a IBiasSvdModel
, you wouldn't even need 2 implementations, just one, and you would pass in the correct instance of ISvdModel
for each situation. This is a design pattern called Inversion of Control, specifically using Dependency Injection, and is very powerful to achieve higher levels of code reuse in your programs.
精彩评论