代码之家  ›  专栏  ›  技术社区  ›  Duncan Bayne

反向和实体框架4.0:如何将EntityCollection指定为IEnumerable?

  •  6
  • Duncan Bayne  · 技术社区  · 16 年前

    我已经指定了两个接口,我使用实体框架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' 无法实现 '演示.icontainer.children' 因为它没有匹配的 返回类型 '系统。集合。泛型。IEnumerable'

    我现在的理解是这是因为 IEnumerable (由EntityCollection实现)是协变的,但可能不是逆变的:

    此类型参数是协变的。也就是说,你可以使用 您指定的类型或其他类型 衍生的。关于协方差和反方差的更多信息, 参见泛型中的协方差和反方差。

    我是对的吗?如果是,是否有任何方法可以实现指定 IContainer 接口仅仅是其他接口而不是使用具体的类?

    或者,我是不是误解了一些更基本的东西?

    3 回复  |  直到 11 年前
        1
  •  5
  •   Jon Skeet    15 年前

    .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> -违反了界面。

        2
  •  4
  •   VinayC    15 年前

    据我所知,您必须实现一个接口——您不能假设协变/反变成员将被作为替代。 即使这是允许的,也要注意,儿童的setter是一个问题。因为它将允许设置类型的属性 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; }
    }
    
        3
  •  0
  •   Timwi    15 年前

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

    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;
            }
        }
    }