簡體   English   中英

在類型參數中具有層次結構的約束泛型

[英]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>盡管MyProviderIProvider<MyElement>MyElementIElement 我可以通過使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>盡管MyProviderIProvider<MyElement>MyElementIElement 為什么它不解析類型參數中的層次結構?

這是一個非常常見的問題。 考慮以下等效問題:

interface IAnimal {}
class Tiger : IAnimal {}
class Giraffe : IAnimal {}
class MyList : IList<Giraffe> { ... }
...
IList<IAnimal> m = new MyList();

現在,你的問題是:“我必須做強制轉換為IList<IAnimal>盡管MyListIList<Giraffe>GiraffeIAnimal為什么這個不行?”

它不起作用,因為...假設它確實有效:

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.

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