tags:

views:

53

answers:

5

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?

+1  A: 

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.

Darin Dimitrov
That takes care of my warning, but then when I do:Foo foo = repos.Get(1);I still get the "call is ambiguous" error.
Jonas
You need to explicitly implement the interface.
Darin Dimitrov
+2  A: 

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

Jon Skeet
Aha...this works. Although I can't see why it doesn't work the way I originally wrote it...enlighten us, Oh Skeety one! :)
Jonas
Ok, but what about explicit implementation?
Steven Sudit
@Jonas: You're not trying to call an actual generic method (i.e. one introducing a new type parameter), so you can't specify a type argument. Instead, you're trying to use an expression as the implementation of a particular generic type... and *that's* what the cast / extra variable is about. (EDIT: In fact, the way you've declared the Get method, it *is* generic - but it shouldn't be.)
Jon Skeet
@Jonas: You could have a generic method `T2 Get<T2>` of course... but then what would the point of `T` be?
Jon Skeet
There's no reason to declare the method T Get<T>(int id). Just change it to T Get(int id)
erash
A: 

Use explicit implementation. To specify which Get, first cast up to the appropriate interface.

Steven Sudit
+1  A: 

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.

x0n
+2  A: 

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.

LBushkin