[英]Constrained generics with hierarchy in type parameter
我在C#中遇到泛型問題我希望你能幫助我。
public interface IElement { }
public interface IProvider<T> where T : IElement {
IEnumerable<T> Provide();
}
到目前為止,它非常簡單。 我希望提供程序返回特定元素的可枚舉。 接口的具體實現如下:
public class MyElement : IElement { }
public class MyProvider : IProvider<MyElement> {
public IEnumerable<MyElement> Provide() {
[...]
}
}
但是當我想要使用它時,問題就出現了。 這不能編譯,因為它無法將MyProvider
隱式轉換為IProvider<IElement>
:
IProvider<IElement> provider = new MyProvider();
我必須對IProvider<IElement>
IProvider<MyElement>
盡管MyProvider
是IProvider<MyElement>
而MyElement
是IElement
。 我可以通過使MyProvider
也實現IProvider<MyElement>
來避免IProvider<MyElement>
,但為什么它不解析type參數中的層次結構?
編輯:根據托馬斯的建議,我們可以使它在T
協變。 但是,如果有其他方法,如下面有類型T
參數?
public interface IProvider<T> where T : IElement {
IEnumerable<T> Provide();
void Add(T t);
}
我必須對
IProvider<IElement>
IProvider<MyElement>
盡管MyProvider
是IProvider<MyElement>
而MyElement
是IElement
。 為什么它不解析類型參數中的層次結構?
這是一個非常常見的問題。 考慮以下等效問題:
interface IAnimal {}
class Tiger : IAnimal {}
class Giraffe : IAnimal {}
class MyList : IList<Giraffe> { ... }
...
IList<IAnimal> m = new MyList();
現在,你的問題是:“我必須做強制轉換為IList<IAnimal>
盡管MyList
是IList<Giraffe>
和Giraffe
是IAnimal
為什么這個不行?”
它不起作用,因為...假設它確實有效:
m.Add(new Tiger());
m是動物名單。 您可以將老虎添加到動物列表中。 但是m實際上是MyList,MyList只能包含長頸鹿! 如果我們允許這樣做,那么你可以將老虎添加到長頸鹿列表中 。
這必須失敗,因為IList<T>
有一個帶有IList<T>
的Add方法。現在,也許你的接口沒有采用T的方法。在這種情況下,你可以將接口標記為協變 ,編譯器將驗證接口對於方差是真正安全的,並允許你想要的方差。
由於T
僅出現在IProvider<T>
界面的輸出位置,因此可以使其在T
IProvider<T>
協變:
public interface IProvider<out T> where T : IElement {
IEnumerable<T> Provide();
}
這將使該指示合法:
IProvider<IElement> provider = new MyProvider();
此功能需要C#4。閱讀泛型中的協方差和逆變量以獲取更多詳細信息。
如果你只使用參考IProvider<IElement>
有訪問方法T
的輸出位置,你可以分離的界面分為兩個(請為他們找到更好的名字,像ISink<in T>
的逆變一個):
public interface IProviderOut<out T> where T : IElement {
IEnumerable<T> Provide();
}
public interface IProviderIn<in T> where T : IElement {
void Add(T t);
}
你的類實現了兩個:
public class MyProvider : IProviderOut<MyElement>, IProviderIn<MyElement> {
public IEnumerable<MyElement> Provide() {
...
}
public void Add(MyElement t) {
...
}
}
但是現在你在需要upcast時使用協變接口:
IProviderOut<IElement> provider = new MyProvider();
或者,您的界面可以繼承:
public interface IProvider<T> : IProviderIn<T>, IProviderOut<T>
where T : IElement {
// you can add invariant methods here...
}
你的類實現它:
public class MyProvider : IProvider<MyElement> ...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.