实现嵌套的泛型接口

14

我有以下的类 / 接口:

// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }

// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<T> where T : IA { }

我尝试使用以下代码创建新实例:

IB<IA> foo = new B();

我收到了以下错误信息:
Cannot implicitly convert type 'B' to 'IB<IA>'. An explicit conversion exists (are you missing a cast?)

请问有人能解释一下为什么这是不可能的吗?

你使用的是哪个版本的C#? - Oded
4
Covariance and Contravariance FAQEric Lippert 的博客系列文章 分别介绍了协变和逆变。我将为您翻译这些内容并尽可能使它们更加通俗易懂,但不会修改原意。 - Oded
1
B是IB<A>,而不是IB<IA>。 - Servy
3个回答

45

好的,让我们用 Fish 替换 A,用 IAnimal 替换 IA,用 Aquarium 替换 B,用 IContainer<T> 替换 IB<T>。我们将在 IContainer<T> 中添加一个成员,并添加 IAnimal 的第二个实现:

// Model
public class Fish : IAnimal { }
public class Tiger : IAnimal { }
// ModelLogic
public class Aquarium : IContainer<Fish> 
{ 
    public Fish Contents { get; set; }
}

// Model Interface
public interface IAnimal { }
// ModelLogic Interface
public interface IContainer<T> where T : IAnimal 
{ 
    T Contents { get; set; }
}

IContainer<IAnimal> foo = new Aquarium(); // Why is this illegal?
foo.Contents = new Tiger(); // Because this is legal!

你可以将老虎放入foo中-- foo 被定义为一个可容纳任何动物的容器。但是你只能将鱼放入水族馆中。因为你可以在Aquarium上执行的操作与你可以在IContainer<IAnimal>上执行的操作不同,所以这些类型不兼容。
你想要的功能称为泛型接口协变性,它在C# 4中被支持,但你必须向编译器证明你永远不会将老虎放入你的鱼缸中。你需要做的是:
// Model
public class A : IA { }
// ModelLogic
public class B : IB<A> { }

// Model Interface
public interface IA { }
// ModelLogic Interface
public interface IB<out T> where T : IA { }

注意IB上的协变注释。这里的out表示T只能用作输出,而不能用作输入。如果T仅为输出,则没有办法将老虎放入鱼缸中,因为没有“放入”属性或方法。
在我们向C#添加该功能时,我撰写了许多博客文章。如果您对该功能涉及的设计考虑感兴趣,请参见: http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

无效的差异:类型参数'T'必须在'xx.IContainer<T>.Contents'上不变地有效。'T'是协变的。我遇到了这个错误。我对协变的东西还很陌生。这个错误是什么意思? - Sandeep
3
@Sandeep:你在输入位置上使用了T,但你之前说过只会在输出位置上使用它。Contents是一个带有setter的属性吗?如果是的话,显然T被用于输入位置,因此接口无法在T上实现协变。 - Eric Lippert
1
在所有与方差相关的问题中,这是迄今为止最有意义的最佳答案。+1 - Cord Rehn

1

要修复您的代码,只需更改

public interface IB<T> where T : IA { }

public interface IB<out T> where T : IA { }

0

当你有空接口时,很难看出来。考虑一下你在接口IB中有一个方法M:

public interface IB<T> where T : IA 
{ 
    void M(T t); 
}

这里是B的实现:

public class B : IB<A>
{
    public void M(A t)
    {
        // only object of type A accepted 
    }
}

然后你有了Object C,它也实现了IA接口:

public class C : IA { } 

所以,如果你的代码是可行的,那么你可以调用:

IB<IA> foo = new B();
foo.M(new C());

问题是B类仅接受A类型的对象。错误!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接