我已经指定了几个接口,我使用Entity Framework 4实现了这些接口。我能想出的最简单的演示代码是:
public class ConcreteContainer : IContainer
{
public EntityCollection<ConcreteChild> Children { get; set; }
}
public class ConcreteChild : IChild
{
}
public interface IContainer
{
IEnumerable<IChild> Children { get; set; }
}
public interface IChild
{
}
我从上面收到以下编译器错误:
'Demo.ConcreteContainer'可以 没有实现接口成员 'Demo.IContainer.Children'。 'Demo.ConcreteContainer.Children' 无法实施 'Demo.IContainer.Children' 因为它没有匹配 返回类型 'System.Collections.Generic.IEnumerable'
我目前的理解是,这是因为IEnumerable(由EntityCollection实现)是协变的,但可能不是逆变的:
此类型参数是协变的。也就是说,你可以使用 您指定的类型或更多的类型 的。有关协方差和逆变的更多信息, 参见泛型中的协方差和逆变。
我是否正确,&amp;如果是这样,有什么方法可以实现我的目标,即纯粹根据其他接口而不是使用具体类来指定IContainer
接口?
或者,我是否误解了一些更基本的东西?
答案 0 :(得分:5)
.NET 4中的泛型差异在这里无关紧要。接口的实现必须在类型方面与接口签名完全匹配。
例如,选择ICloneable
,如下所示:
public interface ICloneable
{
object Clone();
}
能够像这样实现它很好:
public class Banana : ICloneable
{
public Banana Clone() // Fails: this doesn't implement the interface
{
...
}
}
...但.NET不允许这样做。您有时可以使用显式接口实现解决此问题,如下所示:
public class Banana : ICloneable
{
public Banana Clone()
{
...
}
object ICloneable.Clone()
{
return Clone(); // Delegate to the more strongly-typed method
}
}
然而,在你的情况下,你不能这样做。请考虑以下代码,如果ConcreteContainer
被视为实现IContainer
:
IContainer foo = new ConcreteContainer();
foo.Children = new List<IChild>();
现在,您的属性设置器实际上只声明与EntityCollection<ConcreteChild>
一起使用,因此它显然无法与任何 IEnumerable<IChild>
一起使用 - 违反了界面。
答案 1 :(得分:4)
据我所知,你必须实现一个界面 - 你不能假设一个协变/反变体成员将被挑选作为替代。
即使这是允许的,请注意儿童的制定者是一个问题。因为它允许使用任何其他类型的值(例如EntityCollection<ConcreteChild>
或List<ConcreteChild>
)设置 EntityCollection<ConcreteChild2>
类型的属性,因为它们都在实现IEnumerable<IChild>
。
在当前的设计中,我将在ConcreteContainer中私下实现IContainer,并检查IEnumerable.Children setter中的输入值是否为兼容类型。接近这种设计的另一种方法是使用通用接口,例如:
public interface IContainer<T> where T:IChild
{
IEnumerable<T> Children { get; set; }
}
答案 2 :(得分:0)
所以你需要实现这个界面,对吗?
public interface IContainer
{
IEnumerable<IChild> Children { get; set; }
}
但是在真正的类中,您希望属性为EntityCollection<ConcreteChild>
类型。这是你如何做到这一点:
public class ConcreteContainer : IContainer
{
// This is the property that will be seen by code that accesses
// this instance through a variable of this type (ConcreteContainer)
public EntityCollection<ConcreteChild> Children { get; set; }
// This is the property that will be used by code that accesses
// this instance through a variable of the type IContainer
IEnumerable<ConcreteChild> IContainer.Children {
get { return Children; }
set {
var newCollection = new EntityCollection<ConcreteChild>();
foreach (var item in value)
newCollection.Add(item);
Children = newCollection;
}
}
}