views:

129

answers:

2

I have a class similar to the following:

public abstract class Manager<T, TInterface> : IManager<T> where TInterface : IRepository<T>
{
    protected abstract TInterface Repository { get; }

    public virtual List<T> GetAll()
    {
        return Repository.GetAll();
    }
}

This works perfectly fine, however, is there a way to get away from having the TInterface in the abstract class declaration and in the resulting class that extends my generic abstract class:

public class TestManager : Manager<TestObject, ITestRepository>, ITestManager

I am forced to use ITestRepository and make the Repository property abstract due to the fact that it can contain custom methods that I need to know about and be able to call.

As I continue to build layers, I will have to keep doing this process the whole way up the stack. Examples would be if I had a generic abstract controller or service layer:

public class TestService : Service<TestObject, ITestManager>, ITestService

Is there a better way to do this or is this the best practice to allow a generic class to call another generic class?

A: 

No, you can't get around it. You can try, but the result will be ugly and in some way incorrect. The reason is that you are asking generics not to be generic but still be generic.

If a new class uses a generic class, either in inheritance or composition, and it itself does not know enough to specify the type parameters to the generic class it is using, then it must itself be generic. It is analogous the method call chains, where a method may pass parameters along to another method. It can't make up the arguments to the inner method, but must rather take them as parameters itself from a caller that does know what they are. Type parameters are the same.

One thing that does make this feel like code smell is the fact that you can't have a variable of type Manager<,>. It has to be fully type-specified. One solution I've come up with is to have non-generic interfaces that the generic classes implement. These interfaces have as much of the public interface of the generic class as is possible (they can't have methods or properties that reference the type parameters). Then you can pass around variables of the type of the interface and not have to specify type parameters.

Example:

interface IExample {
    string Name { get; }
    void SomeNonGenericMethod(int i);
}

class Example<T> : IExample {
    public string Name { get { ... } }

    public void SomeNonGenericMethod(int i) {
        ...
    }

    public T SomeGenericMethod() {
        ...
    }
}
siride
I had a feeling I would be stuck in this position. I was hoping to be able to somehow 'extend' the implementation of the abstract Repository property to be of the type I needed. I suppose I could still cast it to type 'ITestRepository' when I needed to call custom methods, but that is almost as ugly as the way I have it currently implemented.
Brandon
+1  A: 

It seems that all you want to do is to make Manager<T> testable, and use a mock as a repository that you can query for special members.

If that's the case, maybe you can change your design to this:

public class Manager<T> : IManager<T> {
  protected IRepository<T> Repository { get; set; }
  // ...
  public virtual List<T> GetAll() {
    return Repository.GetAll();
  }
}

Now, all the specifics of testing are in a testing subclass:

public class TestingManager<T> : Manager<T> {
  public new ITestRepository<T> Repository {
    get {
      return (ITestRepository<T>)base.Repository;
    }
    set {
      base.Repository = value;
    }
  }
}

When you write your unit tests, you create TestingManager<T> instances (referenced through TestingManager<T> declared variables and fields), and you provide them with a test repository. Whenever you query their Repository, you'll always get a strongly-typed test repository.

UPDATE:

There's another way to solve this, without a subclass. You declare your repository objects as test repositories that you pass to Manager<T>s and you query them directly, without going through the Manager<T>.

[Test]
public void GetAll_Should_Call_GetAll_On_Repository_Test() {
  var testRepository = new TestRepository();
  var orderManager = new Manager<Order>(testRepository);
  // test an orderManager method
  orderManager.GetAll();
  // use testRepository to verify (sense) that the orderManager method worked
  Assert.IsTrue(testRepository.GetAllCalled);
}
Jordão
I changed my code to look like what you provided and ran my unit tests and they failed. The Manager<T> class methods are looking at the Repository property that returns an IRepository<T> instance. Since that property is not set and instead the 'public new ITestRepository<T> Repository' property is set, it is getting a NullReferenceException in regards to the Repository property in the Manager<T> abstract class.
Brandon
Sorry, I forgot to use `base` on the subclass property. But don't take my code as the whole implementation, just as an idea.
Jordão
Adding the base to the Repository call made everything work perfectly. This is an ideal solution, however, since I had to remove the abstract modifier off of the Manager<T>'s Repository property, the implementation is not forced at the TestManager<T> level. This is fine, because if no custom methods exist, there is no need for it to be implemented.
Brandon