我有一些通用接口相互链接。
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'。
在这种情况下,如何正确使用通用接口?
答案 0 :(得分:7)
T
中的{p> IC<T>
必须是IB<IA>
。你给它一个IB<A>
。您无法保证将IB<A>
用作IB<IA>
,因为A
实施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#中查找有关“协方差和逆变”的问题,以获取大量示例。
答案 1 :(得分:3)
我不知道它是否可以更容易完成(以及更多 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”到的隐式引用转换 'ConsoleApplication2.IB'。
这真的很酷,不是吗?