Home > Software design >  Inherit from interface with different property type
Inherit from interface with different property type

Time:01-05

I would like to do something like below in c#. Is there any trick to do it?

interface IGenericRepository<T> {
    
}

interface IModel<T>{
    IGenericRepository<T> Repository { get; set; }
}

class ConcreteRepository : IGenericRepository<ConcreteModel>{

}

class ConcreteModel : IModel<ConcreteModel> {
    ConcreteRepository Repository { get; set; } /// this doesn't work
    /// IGenericRepository<ConcreateModel> Repository { get; set; } this would work 
}

I fixed question.

CodePudding user response:

Please note, that since ConcreteModel implements IModel<ConcreteModel> then ConcreteModel.Repository must accept any class which implements IModel<ConcreteModel> not necessary ConcreteRepository, i.e. the code below must be legal:

// Note, that MyOwnModel is not ConcreteModel
// But MyOwnModel implements IModel<ConcreteModel> 
public class MyOwnModel : IModel<ConcreteModel> {
  ...
}

...

ConcreteModel demo = new();

// Must be valid, since MyOwnModel does implement Model<ConcreteModel>
demo.Repository = new MyOwnModel();

If you want to have ConcreteRepository Repository while having IModel<ConcreteModel> implemented you can implement the interface explicitly:

class ConcreteModel : IModel<ConcreteModel> {
  ConcreteRepository Repository { get; set; } 
            
  IGenericRepository<ConcreteModel> IModel<ConcreteModel>.Repository {
    get => Repository;
    set {
      //TODO: you should do something if value IS NOT ConcreteRepository
      // Here I've supposed that exception should be thrown
      Repository = value is ConcreteRepository concrete 
        ? concrete 
        : throw new ArgumentException("Value must be of ConcreteRepository type!");
    }
  }
}

CodePudding user response:

In short - no, this would not be type safe. Imagine if you could - then the following would be possible:

class ConcreteRepository1 : IGenericRepository<ConcreteModel>{}

IModel<ConcreteModel> x = new ConcreteModel();
x.Repository = new ConcreteRepository1(); // but ConcreteModel works only with `ConcreteRepository`

What you can do - remove setter from interface (so the code is still type safe and you don't need type checks) and explicitly implement it:

interface IModel<T>
{
    IGenericRepository<T> Repository { get; }
}

class ConcreteModel : IModel<ConcreteModel>
{
    public ConcreteRepository Repository { get; set; }
    IGenericRepository<ConcreteModel> IModel<ConcreteModel>.Repository => Repository;
}
  • Related