接口通用层次结构
本文关键字:层次结构 接口 | 更新日期: 2023-09-27 18:02:26
我有一些通用接口彼此链接。
public interface IA
{
int val { get; set; }
}
public interface IB<T> where T:IA
{
T a_val { get; set; }
}
public interface IC<T> where T : IB<IA>
{
T b_val { get; set; }
}
public class a:IA
{
public int val { get; set; }
}
public class b:IB<a>
{
public a a_val { get; set; }
}
public class c:IC<b>
{
public b b_val { get; set; }
}
对于最后一个类c,我有一个错误:
类型"b"不能用作泛型类型中的类型参数"T"或方法"IC"。没有来自"b"的隐式引用转换至"IB"。
在这种情况下,如何正确使用通用接口?
IC<T>
中的T
必须是IB<IA>
。你给了它一个IB<A>
。不能仅仅因为A
实现了IA
就保证IB<A>
可以用作IB<IA>
。
可以这样想:如果IB<T>
的意思是"我可以吃任何T型的东西",IA
的意思是"我是一种水果",A
的意思是苹果",那么IB<IA>
的意思是:"我可以吃任意水果",而IB<A>
的意思是我可以吃任何苹果"。如果某个代码想要喂你香蕉和葡萄,那么它需要IB<IA>
,而不是IB<A>
。
让我们假设IB<A>
可以转换为IB<IA>
,看看哪里出了问题:
class AppleEater : IB<Apple>
{
public Apple a_val { get; set; }
}
class Apple : IA
{
public int val { get; set; }
}
class Orange : IA
{
public int val { get; set; }
}
...
IB<Apple> iba = new AppleEater();
IB<IA> ibia = iba; // Suppose this were legal.
ibia.a_val = new Orange(); // ibia.a_val is of type IA and Orange implements IA
现在,我们只需将iba.val
(Apple
类型的属性(设置为对Orange
类型对象的引用。
这就是为什么转换必须是非法的。
那么,你如何使这合法化呢?
就目前的代码而言,你不能,因为正如我刚才所展示的,它不是类型安全的。
您可以将T
标记为out
,使其合法,如下所示:interface IB<out T>
。但是,在任何"输入上下文"中使用T
都是非法的。特别是,不能有任何具有setter的T
类型的属性。如果我们进行该限制,那么问题就会消失,因为a_val
不能设置为Orange
的实例,因为它是只读的。
这个问题在SO上被问得非常频繁。请在C#中查找有关"协方差和反方差"的问题,以获取大量示例。
我不知道是否可以更容易地完成(以及更多的clean(,但这段代码编译:
public interface IA
{
int val { get; set; }
}
public interface IB<T> where T : IA
{
T a_val { get; set; }
}
public interface IC<T, U> where T : IB<U> where U : IA
{
T b_val { get; set; }
}
public class a : IA
{
public int val { get; set; }
}
public class b : IB<a>
{
public a a_val { get; set; }
}
public class c : IC<b, a>
{
public b b_val { get; set; }
}
更重要的是,它不会让你做这样的事情:
public class a1 : IA
{
public int val { get; set; }
}
public class c : IC<b, a1>
{
public b b_val { get; set; }
}
编译器抛出以下错误:
类型"ConsoleApplication2.b"不能用作类型参数"T"在泛型类型或方法"ConsoleApplication2.IC"中没有从"ConsoleApplication2.b"到的隐式引用转换'控制台应用程序2.IB'.
这真的很酷,不是吗?