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

5
我正在尝试做类似于这样的事情:
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个回答

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方法变为非泛型方法。目前,您正在尝试重新声明IRepository<T>.Get<T>中的类型参数T。您只需使用IRepository<T>的现有类型参数即可。


啊哈...这个好使。虽然我看不出来为什么按照我最初的写法它不好用...启迪我们吧,哦,Skeety大人! :) - Jonas
好的,但是显式实现呢? - Steven Sudit
@Jonas:你并不是在尝试调用一个实际的泛型方法(即引入新类型参数的方法),因此你不能指定类型参数。相反,你正在尝试使用一个表达式作为特定泛型类型的实现...这就是强制转换/额外变量的原因。(编辑:事实上,你声明Get方法的方式是泛型的 - 但它不应该是这样的。) - Jon Skeet
@Jonas:当然,你可以有一个通用方法T2 Get<T2>...但那么T的意义是什么呢? - Jon Skeet
没有必要声明方法 T Get<T>(int id)。只需将其更改为 T Get(int id)。 - cordialgerm

4

很遗憾,你不能这样做。在C#中,泛型不是设计成这样工作的。如果你按照这种模式,你将被迫始终通过强制转换repo来消除调用哪个接口版本的Get()歧义:

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

在方法声明中,您不需要再次重复T。它已经在接口中声明:

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

还要注意,您需要显式实现 IFooBarRepository 接口,因为仅返回类型不同的 Get 方法是不可能的。请参考:http://msdn.microsoft.com/en-us/library/44a9ty12.aspx

这解决了我的警告问题,但是当我执行以下操作时:Foo foo = repos.Get(1);我仍然会收到“调用不明确”的错误提示。 - Jonas
你需要明确地实现接口。 - Darin Dimitrov

1

改为:

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

使用

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

这有点违背使用泛型来避免强制转换的整体感觉,但不幸的是,如果没有向编译器提供更多提示,你所做的事情是不可能的。


0

使用显式实现。要指定哪个Get,首先向上转换到适当的接口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接