6

我已经指定了几个接口,我正在使用 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 实现)是协变的,但可能不是逆变的:

此类型参数是协变的。也就是说,您可以使用您指定的类型或任何派生度更高的类型。有关协变和逆变的更多信息,请参阅泛型中的协变和逆变。

我是否正确,如果是这样,有什么方法可以实现我的目标,即IContainer纯粹根据其他接口而不是使用具体类来指定接口?

或者,我是否误解了更基本的东西?

4

3 回答 3

5

.NET 4 中的通用差异在这里无关紧要。接口的实现必须在类型方面与接口签名完全匹配。

例如, take 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>- 违反接口。

于 2010-08-22T13:30:00.457 回答
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; }
}
于 2010-08-18T06:36:25.830 回答
0

所以你需要实现这个接口,对吧?

public interface IContainer
{
    IEnumerable<IChild> Children { get; set; }
}

但是在真正的类中,你希望属性是 type 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;
        }
    }
}
于 2010-08-23T01:03:01.910 回答