让我们说C#中有这些泛型类型:
class Entity<KeyType>
{
public KeyType Id { get; private set; }
...
}
interface IRepository<KeyType, EntityType> where EntityType : Entity<KeyType>
{
EntityType Get(KeyType id);
...
}
以及这些具体类型:
class Person : Entity<int> { ... }
interface IPersonRepository : IRepository<int, Person> { ... }
现在PersonRepository
的定义是多余的:明确说明了KeyType
的{{1}}为Person
的事实,尽管可以从int
Person
{ {1}}是Entity<int>
的子类型。
能够像这样定义IPersonRepository
会很高兴:
interface IPersonRepository : IRepository<Person> { ... }
让编译器确定KeyType
是int
。有可能吗?
答案 0 :(得分:1)
我们想说声明
interface IPersonRepository : IRepository<Person> { }
这将要求存在一个带有一个类型参数IRepository<EntityType>
的通用接口。
interface IRepository<EntityType> where EntityType : Entity<KeyType>
{
EntityType Get(KeyType id);
}
在第一行的末尾,您引用了一个名为KeyType
的东西,该东西尚未被声明或定义。没有名为&#34; KeyType&#34;。
这会有效:
interface IRepository<EntityType> where EntityType : Entity<int>
{
EntityType Get(int id);
}
或者这个:
interface IRepository<EntityType> where EntityType : Entity<string>
{
EntityType Get(string id);
}
但是,当然,你不能同时拥有两个相互矛盾的定义。显然,您对此并不满意,因为您希望能够以与其他键类型一起使用的方式定义您的IRpository接口。
嗯,你可以,如果你在密钥类型中使它成为通用的:
interface IRepository<KeyType, EntityType> where EntityType : Entity<KeyType>
{
EntityType Get(KeyType id);
}
还有另一种方法:
interface IRepository<KeyType>
{
EntityType<KeyType> Get(KeyType id);
}
现在你可以定义
class PersonRepository : IRepository<int>
{
public EntityType<int> Get(int id) { ... }
}
显然,你不会对此感到满意,因为你想声明Get方法必须返回Person
,而不仅仅是Entity<int>
。
唯一解决方案中具有两个类型参数的通用接口。实际上,它们之间存在必要的关系,如约束中所表达的那样。但这里没有冗余:为类型参数指定int
并没有提供足够的信息。
如果我们说
class PersonRepository : IRepository<int, Person>
{
public Person Get(int id) { ... }
}
确实存在冗余:当已经指定了类型参数int
时,指定类型参数Person
是多余的。
可以使用可以推断KeyType的语法来进行操作。例如,Patrick Hoffman建议:
class PersonRepository : IRepository<EntityType: Person>.
{
public Person Get(int id) { ... }
}
虽然理论上可行,但我担心这会给语言规范和编译器增加很多复杂性,但收益很少。事实上,有任何收益吗?你肯定不会保存击键!比较这两个:
// current syntax
class PersonRepository : IRepository<int, Person>
{
public Person Get(int id) { ... }
}
// proposed syntax
class PersonRepository : IRepository<EntityType: Person>
{
public Person Get(int id) { ... }
}
语言就是这样,对我来说看起来并不太糟糕。
答案 1 :(得分:1)
不,C#的类型系统还不够先进,无法表达你想要的东西。所需的功能称为更高的kinded类型,它们通常在强类型函数语言(Haskell,OCaml,Scala)中找到。
回过头来,你希望能够写出来
interface IRepository<EntityType<EntityKey>> {
EntityType<EntityKey> Get(KeyType id);
}
interface PersonRepository : IRepository<Person> {
Person Get(Int id);
}
但是在C#中没有办法表达EntityType类型,换句话说,类型参数有一些泛型参数,并在代码中使用该泛型参数。
旁注:存储库模式是邪恶的,必须在火灾中死亡。
答案 2 :(得分:0)
不,这是不可能的,因为它不能推断出类型(编译器没有)。
应该可以这样做(你需要成为C#编译器团队的一部分才能获得它),因为没有其他值可能将KeyType
的值放入type参数中Entity
。您不能放入派生类型或基类类型。
正如其他人评论的那样,它可能使代码过于复杂。此外,这仅适用于Entity<T>
是一个类的情况,当它是一个界面时,它可以推断出类型,因为它可以有多个实现。 (也许这是他们没有建立起来的最终原因)