how to reference the Type in the Concrete class of a generic class

536 Views Asked by At

I found the following example, to which I have a follow up question.

stack overflow question

the existing code from the question is

 public interface IRepository<T> where T : EntityObject
 {
    RepositoryInstructionResult Add(T item);
    RepositoryInstructionResult Update(T item);
    RepositoryInstructionResult Delete(T item);
 }

 public class Repository<T> : IRepository<T> where T : EntityObject
 {
    virtual RepositoryInstructionResult Add(T item)
    { //implementation}
    virtual RepositoryInstructionResult Update(T item);
    { //implementation}
    virtual RepositoryInstructionResult Delete(T item);
    { //implementation}
  }

public class BarRepository : Repositorybase<Bar>
 {
    public override RepositoryInstructionResult Update(Bar item);
    {
       //Call base method if needed
       //Base.Update(item);

       //implement your custom logic here
    }
  }

what I would like to do is change the Update method to something like

public class BarRepository : Repositorybase<Bar>
 {
    // T is of Type Bar
    public override RepositoryInstructionResult Update(T item);
    {
       //implement your custom logic here
    }
  }

Question: is there a way to expose the generic type in BarResposity : Repositorybase<Bar> to the methods in BarRepository?

looking for a better alternative to "search and replace" when building out the concrete class (eg make a copy of BarRespository as FooRepository and change all references from Bar to Foo). I would rather change the type in one place only.

(edit) Usage needs to remain as

var obj = new BarRepository();
3

There are 3 best solutions below

3
D Stanley On BEST ANSWER

It's a bit of a hack, but you could use a using alias to define the entity type:

 using MyType = Bar;

 public class BarRepository : Repositorybase<MyType>
 {
    public override RepositoryInstructionResult Update(MyType item);
    {
   
       return base.Update(item);
    }
  }

Now when you copy to Foo.cs, you can just change the using directive to

 using MyType = Foo;

But, I would look to try and reuse as much generic code as possible, as it's not clear at all what MyType is just by looking at the methods. There's nothing wrong with a find.replace to define a new repository type that customizes actions - you just want to keep the repeated to a minimum.

9
Serge On

IMHO any GENERIC repository is a waste of time, but if you decide to use it , what is wrong with this

public class BarRepository<T> : Repository<Bar> where T : class
{
    public override void Update(Bar item)
    {

    }
    
    public  void Update(T item)
    {
     
    }
}

UPDATE

It looks weird but since OP wants it , you can create this code too

public class BarRepository<T> : Repository<Bar> where T : Bar
{
    public override void Update(Bar item)
    {
        Console.WriteLine("it is Bar");
    }
    // T is of Type Bar
    public void Update(T item)
    {
          Console.WriteLine("it is T");
    }
}

tests

public class Bar { }
public class NoBar { }

var barRep= new BarRepository<Bar>();

barRep.Update(new Bar()); // "it is T"

var noBarRep= new BarRepository<NoBar>(); // ERROR!

UPDATE 2

since if you want

var rep = new BarRepository();

var result= rep.Update(new Bar());

you can do it the way you have done already

public class BarRepository : Repositorybase<Bar>
 {
    public override RepositoryInstructionResult Update(Bar item);
    {
   
       return base.Update(item);
    }
  }

PS

if you are still not satisfied, look at the beginning of my answer and forget about the generic repositories. Use the custom ones as the most proffesional developes do.

2
Rom Haviv On

Just as a note, if you're going to override all Add/Update/Delete anyways, you can make them as abstract in the RepositoryBase and then the vs suggestion is your friend: enter image description here

and if there is shared logic between all of concrete classes you can put it in the abstract class and override abstract protected methods instead.

edit: op asked for code that can do it.. well this should work.. but if you want a new concrete implementation, you'll have to create 2 classes now

public class BarRepository<T> : RepositoryBase<T> where T : Bar
{
    public override int Add(T item)
    {
        throw new NotImplementedException();
    }
    public override int Update(T item)
    {
        throw new NotImplementedException();
    }
    public override int Delete(T item)
    {
        throw new NotImplementedException();
    }
}

public class BarRepository : BarRepository<Bar>
{

}

also, if classes are so similar that copy-paste and replace is enough, maybe logic shouldn't be in separate classes but in the generic class? could you give an example of 2 classes maybe?

edit 2: another dirty trick would be to use lambdas, though personally I don't know if I would do it:

public abstract class RepositoryBase<T>
{
    public Func<T, int> Add { get; protected set; }
    public Func<T, int> Update { get; protected set; }
    public Func<T, int> Delete { get; protected set; }
}
public class BarRepository : RepositoryBase<Bar>
{
    public BarRepository()
    {
        Add = i => 6;
        Update = i => 7;
        Delete = i => 8;
    }
}