Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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, int 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.

like image 659
gligoran Avatar asked Aug 18 '11 15:08

gligoran


3 Answers

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) { }
}
like image 163
Jonathan Dickinson Avatar answered Sep 28 '22 10:09

Jonathan Dickinson


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;
        }
    }
like image 30
Jon Raynor Avatar answered Sep 28 '22 10:09

Jon Raynor


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.

like image 44
Matt Greer Avatar answered Sep 28 '22 10:09

Matt Greer