Jul*_*ano 6 c# generics parameters types hierarchy
我在C#中遇到泛型问题我希望你能帮助我.
public interface IElement { }
public interface IProvider<T> where T : IElement {
IEnumerable<T> Provide();
}
Run Code Online (Sandbox Code Playgroud)
到目前为止,它非常简单.我希望提供程序返回特定元素的可枚举.接口的具体实现如下:
public class MyElement : IElement { }
public class MyProvider : IProvider<MyElement> {
public IEnumerable<MyElement> Provide() {
[...]
}
}
Run Code Online (Sandbox Code Playgroud)
但是当我想要使用它时,问题就出现了.这不会编译,因为它无法隐式转换MyProvider为IProvider<IElement>:
IProvider<IElement> provider = new MyProvider();
Run Code Online (Sandbox Code Playgroud)
我必须做一个演员,IProvider<IElement>尽管MyProvider是一个IProvider<MyElement>并且MyElement是一个IElement.我可以通过制作MyProvider实现来避免强制转换IProvider<MyElement>,但为什么它不能解析类型参数中的层次结构?
编辑:根据托马斯的建议,我们可以使其协变T.但是,如果有其他方法,如下面有类型的参数T怎么办?
public interface IProvider<T> where T : IElement {
IEnumerable<T> Provide();
void Add(T t);
}
Run Code Online (Sandbox Code Playgroud)
我必须做一个演员,
IProvider<IElement>尽管MyProvider是一个IProvider<MyElement>并且MyElement是一个IElement.为什么它不解析类型参数中的层次结构?
这是一个非常常见的问题.考虑以下等效问题:
interface IAnimal {}
class Tiger : IAnimal {}
class Giraffe : IAnimal {}
class MyList : IList<Giraffe> { ... }
...
IList<IAnimal> m = new MyList();
Run Code Online (Sandbox Code Playgroud)
现在你的问题是:"我必须做一个演员,IList<IAnimal>尽管这MyList是一个IList<Giraffe>并且Giraffe是一个IAnimal.为什么这不起作用?"
它不起作用,因为...假设它确实有效:
m.Add(new Tiger());
Run Code Online (Sandbox Code Playgroud)
m是动物名单.您可以将老虎添加到动物列表中.但是m实际上是MyList,MyList只能包含长颈鹿!如果我们允许这样做,那么你可以将老虎添加到长颈鹿列表中.
这必须失败,因为IList<T>有一个带有T的Add方法.现在,也许你的接口没有采用T的方法.在这种情况下,你可以将接口标记为协变,编译器将验证接口是否真正安全方差并允许您想要的方差.
由于T只出现在IProvider<T>界面的输出位置,因此可以使其在T以下位置变为协变:
public interface IProvider<out T> where T : IElement {
IEnumerable<T> Provide();
}
Run Code Online (Sandbox Code Playgroud)
这将使该指示合法:
IProvider<IElement> provider = new MyProvider();
Run Code Online (Sandbox Code Playgroud)
此功能需要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);
}
Run Code Online (Sandbox Code Playgroud)
你的类同时实现了:
public class MyProvider : IProviderOut<MyElement>, IProviderIn<MyElement> {
public IEnumerable<MyElement> Provide() {
...
}
public void Add(MyElement t) {
...
}
}
Run Code Online (Sandbox Code Playgroud)
但现在,当您需要向上转换时,您可以使用协变接口:
IProviderOut<IElement> provider = new MyProvider();
Run Code Online (Sandbox Code Playgroud)
或者,您的接口可以从两者继承:
public interface IProvider<T> : IProviderIn<T>, IProviderOut<T>
where T : IElement {
// you can add invariant methods here...
}
Run Code Online (Sandbox Code Playgroud)
你的类实现了它:
public class MyProvider : IProvider<MyElement> ...
Run Code Online (Sandbox Code Playgroud)