开发者

Contravariance and Entity Framework 4.0: how to specify EntityCollection as IEnumerable?

开发者 https://www.devze.com 2022-12-22 04:56 出处:网络
I have specified a couple of interfaces, which I am implementing as entities using Entity Framework开发者_Python百科 4.The simplest demonstration code I can come up with is:

I have specified a couple of interfaces, which I am implementing as entities using Entity Framework开发者_Python百科 4. The simplest demonstration code I can come up with is:

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

I receive the following compiler error from the above:

'Demo.ConcreteContainer' does not implement interface member 'Demo.IContainer.Children'. 'Demo.ConcreteContainer.Children' cannot implement 'Demo.IContainer.Children' because it does not have the matching return type of 'System.Collections.Generic.IEnumerable'

My current understanding is that this is because IEnumerable (which is implemented by EntityCollection) is covariant but presumably not contravariant:

This type parameter is covariant. That is, you can use either the type you specified or any type that is more derived. For more information about covariance and contravariance, see Covariance and Contravariance in Generics.

Am I correct, & if so, is there any way I can achieve my goal of specifying the IContainer interface purely in terms of other interfaces rather than using concrete classes?

Or, am I misunderstanding something more fundamental?


The generic variance in .NET 4 is irrelevant here. The implementation of an interface has to match the interface signature exactly in terms of types.

For example, take ICloneable, which looks like this:

public interface ICloneable
{
    object Clone();
}

It would be nice to be able to implement it like this:

public class Banana : ICloneable
{
    public Banana Clone() // Fails: this doesn't implement the interface
    {
        ...
    }
}

... but .NET doesn't allow this. You can sometimes use explicit interface implementation work around this, like so:

public class Banana : ICloneable
{
    public Banana Clone()
    {
        ...
    }

    object ICloneable.Clone()
    {
        return Clone(); // Delegate to the more strongly-typed method
    }
}

However, in your case you can't ever do that. Consider the following code, which would be valid if ConcreteContainer was considered to implement IContainer:

IContainer foo = new ConcreteContainer();
foo.Children = new List<IChild>();

Now your property setter is actually only declared to work with EntityCollection<ConcreteChild>, so it clearly can't work with any IEnumerable<IChild> - in violation of the interface.


As far as I understand, you must implement an interface - you can't assume that a covariant/contra-variant member would be picked up as a substitute. Even if it was permissible, note that setter for children is an issue. Because it will allow to set property of type EntityCollection<ConcreteChild> with value of any other type such as List<ConcreteChild> or EntityCollection<ConcreteChild2> because both are implementing IEnumerable<IChild>.

In current design, I will implement IContainer privately in ConcreteContainer and check the input value in IEnumerable.Children setter for a compatible type. Another way to approach this design is to have generic interfaces such as:

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


So you need to implement this interface, right?

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

But in the real class, you want the property to be of type EntityCollection<ConcreteChild>. Here’s how you can do this:

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

精彩评论

暂无评论...
验证码 换一张
取 消