实现多个通用接口 - 类型错误

时间:2010-09-23 20:06:21

标签: c# .net generics

我正在尝试做这样的事情:

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);

我收到了警告:

类型参数“T”与外部类型“IRepository”

中的类型参数同名

错误:

以下方法或属性之间的调用不明确:'IRepository.Get(int)'和'IRepository.Get(int)'

关于如何使这种模式发挥作用的任何想法?

5 个答案:

答案 0 :(得分:7)

要调用适当的方法,您需要让编译器以适当的方式考虑表达式:

IFooBarRepository repo = SomeMethodThatGetsTheActualClass();
IRepository<Foo> fooRepo = repo;
Foo foo = fooRepo.Get(1);

请注意,可以将其转换为一个语句:

IFooBarRepository repo = SomeMethodThatGetsTheActualClass();
Foo foo = ((IRepository<Foo>)repo).Get(1);

......但这看起来很丑陋。

处理调用方法。 在一个类中实现两个接口是下一个障碍......因为它们在参数方面具有相同的签名。你必须明确地实现其中至少一个 - 如果你同时做到这两点,它可能会减少混淆:

public class FooBarRepository : IFooBarRepository
{
    Foo IRepository<Foo>.Get(int id)
    {
        return new Foo();
    } 

    Bar IRepository<Bar>.Get(int id)
    {
        return new Bar();
    } 
}

编辑:您还需要将Get设为非通用方法:目前您正在尝试重新声明T中的类型参数IRepository<T>.Get<T>;您只想使用IRepository<T>现有类型参数。

答案 1 :(得分:2)

不幸的是,你不能。这不是泛型设计用于C#的方式。如果您使用此模式,则您将被迫通过强制转换Get()来消除您希望调用repo的界面版本的歧义:

IFooBarRepository repo = SomeMethodThatGetsTheActualClass(); 
Foo foo = ((IRepository<Foo>)repo).Get(1); 

这可能不是你想要的。

当然,你可以在IFooBarRepository的实现中实现代理方法,返回正确的类型......但同样,这可能不是你想要的。

但是,您可以在IFooBarRepository上创建可以改善语法的属性:

interface IFooBarRepository : IRepository<Foo>, IRepository<Bar>
{
    IRepository<Foo> FooGetter { get; }
    IRepository<Bar> BarGetter { get; }
}

现在你可以写:

IFooBarRepository repo = SomeMethodThatGetsTheActualClass(); 
Foo foo = repo.FooGetter.Get(1); 
Bar bar = repo.BarGetter.Get(2);

通常,希望避免不接受作为类型参数类型的形式参数参数的泛型方法。在您的情况下,您尝试将存储库的语义直接编码到类型系统中。您可能最好将此可重用性划分为表示存储库行为的类型和表示获取对象行为的单独类型。

答案 2 :(得分:1)

您不需要在方法声明中再次重复T。它已在界面声明:

public interface IRepository<T>
{
    T Get(int id);
}

另请注意,您需要explicitly implement IFooBarRepository接口,因为只有Get方法的返回类型不同,这是不可能的。

答案 3 :(得分:1)

而不是:

Foo foo = repo.Get<Foo>(1);

使用

Foo foo = ((IRepository<Foo>)repo).Get(1);

使用泛型来避免转换的总体感觉失败了,但不幸的是,如果不向编译器提供更多提示,那么你所做的事情是不可能的。

答案 4 :(得分:0)

使用显式实现。要指定哪个Get,请先转换到相应的界面。