C#中的泛型类型推断

时间:2014-09-22 09:06:07

标签: c# generics type-inference

让我们说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> { ... }

让编译器确定KeyTypeint。有可能吗?

3 个答案:

答案 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>是一个类的情况,当它是一个界面时,它可以推断出类型,因为它可以有多个实现。 (也许这是他们没有建立起来的最终原因)