繁体   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