簡體   English   中英

C#中的泛型類型推斷

[英]Generic Type Inference in C#

假設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的定義是多余的: PersonKeyTypeint的事實是明確說明的,盡管它可以從PersonEntity<int>的子類型的事實推斷出來。

能夠像這樣定義IPersonRepository會很高興:

interface IPersonRepository : IRepository<Person> { ... }

讓編譯器弄清楚KeyTypeint 可能嗎?

假設我們要申報

interface IPersonRepository : IRepository<Person> { }

這將要求存在具有一個類型參數IRepository<EntityType>的通用接口。

interface IRepository<EntityType> where EntityType : Entity<KeyType>
{
    EntityType Get(KeyType id);
}

在第一行的末尾,您引用一個名為KeyType的東西,該東西尚未聲明或定義。 沒有稱為“KeyType”的類型。

這會工作:

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>

在唯一解決方案中具有兩個類型參數的通用接口。 實際上,它們之間存在必要的關系,如約束中所表達的那樣。 但是這里沒有冗余:為type參數指定int不會攜帶足夠的信息。

如果我們說

class PersonRepository : IRepository<int, Person>
{
    public Person Get(int id) { ... }
}

確實有冗余:當已經指定了類型參數Person時,指定類型參數int是多余的。

可以使用可以推斷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) { ... }
}

語言就是它,它對我來說並不是太糟糕。

不,C#的類型系統不夠先進,無法表達你想要的東西。 所需的特征稱為高級類型 ,通常在強類型函數語言(Haskell,OCaml,Scala)中找到。

回過頭來,你希望能夠寫作

interface IRepository<EntityType<EntityKey>> {
  EntityType<EntityKey> Get(KeyType id);
}

interface PersonRepository : IRepository<Person> {
  Person Get(Int id);
}

但在C#中,無法表達實體類型,換句話說,類型參數具有一些通用參數,並在代碼中使用該通用參數。

旁注:存儲庫模式是邪惡的,必須在火災中死亡。

不,這是不可能寫的,因為它不推斷類型(編譯器沒有)。

應該可以這樣做(你需要成為C#編譯器團隊的一部分才能獲得它),因為沒有其他值可能將KeyType的值放入Entity的type參數中。 您不能放入派生類型或基類類型。

正如其他人評論的那樣,它可能使代碼復雜化。 此外,這僅適用於Entity<T>是一個類的情況,當它是一個接口時,它無法推斷出類型,因為它可以有多個實現。 (也許這是他們沒有建立這個的最終原因)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM