Reputation: 4584
I'm trying to do something like this:
public interface IRepository<T>
{
T Get<T>(int id);
}
public interface IFooBarRepository : IRepository<Foo>, IRepository<Bar>
{
}
IFooBarRepository repo = SomeMethodThatGetsTheActualClass();
Foo foo = repo.Get<Foo>(1);
I'm getting a warning:
Type parameter 'T' has the same name as the type parameter from outer type 'IRepository'
And an error:
The call is ambiguous between the following methods or properties: 'IRepository.Get(int)' and 'IRepository.Get(int)'
Any thoughts on how I can make this pattern work?
Upvotes: 5
Views: 4904
Reputation: 131676
Unfortunately, you can't. This isn't how generics are designed to work in C#. If you go with this pattern, you will be forced to always disambiguiate which interface version you wish to call Get()
on by casting repo
:
IFooBarRepository repo = SomeMethodThatGetsTheActualClass();
Foo foo = ((IRepository<Foo>)repo).Get(1);
which probably isn't what you want.
You could, of course, implement proxying methods in the implementation of IFooBarRepository
that return the right types ... but again, this may not be what you're looking for.
You can, however, create properties on IFooBarRepository
that improve the syntax thusly:
interface IFooBarRepository : IRepository<Foo>, IRepository<Bar>
{
IRepository<Foo> FooGetter { get; }
IRepository<Bar> BarGetter { get; }
}
Now you can write:
IFooBarRepository repo = SomeMethodThatGetsTheActualClass();
Foo foo = repo.FooGetter.Get(1);
Bar bar = repo.BarGetter.Get(2);
In general, it's desirable to avoid generic methods that don't accept as a formal parameters arguments of the types of the type parameters. In your case, you're trying to encode the semantics of repositories directly into the type system. You may be better off dividing this reponsibility into a type that expresses the behavior of the repository and a separate type that expresses the behavior of fetching objects.
Upvotes: 4
Reputation: 52420
Instead of:
Foo foo = repo.Get<Foo>(1);
use
Foo foo = ((IRepository<Foo>)repo).Get(1);
Which kind of defeats the overall feeling of using generics to avoid casting, but unfortunately what you're doing is not possible without providing more hints to the compiler.
Upvotes: 1
Reputation: 19620
Use explicit implementation. To specify which Get
, first cast up to the appropriate interface.
Upvotes: 0
Reputation: 1500415
To call the appropriate one, you'll need to make the compiler think of the expression in the appropriate way:
IFooBarRepository repo = SomeMethodThatGetsTheActualClass();
IRepository<Foo> fooRepo = repo;
Foo foo = fooRepo.Get(1);
Note that you could just cast it in one statement:
IFooBarRepository repo = SomeMethodThatGetsTheActualClass();
Foo foo = ((IRepository<Foo>)repo).Get(1);
... but that looks pretty ugly to me.
That deals with calling the method. Implementing both interfaces in one class is the next hurdle... because they'll have the same signature in terms of parameters. You'll have to implement at least one of them explicitly - and it might cause less confusion if you did both:
public class FooBarRepository : IFooBarRepository
{
Foo IRepository<Foo>.Get(int id)
{
return new Foo();
}
Bar IRepository<Bar>.Get(int id)
{
return new Bar();
}
}
EDIT: You'll also need to make Get
a non-generic method: currently you're trying to redeclare the type parameter T
in IRepository<T>.Get<T>
; you just want to use the existing type parameter of IRepository<T>
.
Upvotes: 7
Reputation: 1038770
You don't need to repeat T
once again at the method declaration. It is already declared at the interface:
public interface IRepository<T>
{
T Get(int id);
}
Also notice that you need to explicitly implement the IFooBarRepository
interface because only the return type of the Get method differs which is not possible.
Upvotes: 2