开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜