接口通用层次结构

时间:2013-03-04 19:26:05

标签: c# generic-interface

我有一些通用接口相互链接。

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'。

在这种情况下,如何正确使用通用接口?

2 个答案:

答案 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'。

这真的很酷,不是吗?