[英]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
的定義是多余的: Person
的KeyType
是int
的事實是明確說明的,盡管它可以從Person
是Entity<int>
的子類型的事實推斷出來。
能夠像這樣定義IPersonRepository
會很高興:
interface IPersonRepository : IRepository<Person> { ... }
讓編譯器弄清楚KeyType
是int
。 可能嗎?
假設我們要申報
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.