2010-03-03 4 views
6

내가 가장 간단한 데모 코드는 내가 가지고 올 수 내가 엔티티 프레임 워크 4를 사용하여 개체로 구현하고 인터페이스의 몇 가지를 지정한 것입니다 : 나는 위에서 다음과 같은 컴파일러 오류가 발생Contravariance 및 Entity Framework 4.0 : EntityCollection을 IEnumerable로 지정하는 방법?

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'

나의 현재 이해의 일치 반환 형식이 없기 때문에

이 유형의 매개 변수는 공변입니다 : (EntityCollection에 의해 구현됩니다) IEnumerable이 공변하지만 아마도 contravariant 때문이이 것입니다. 즉, 은 지정한 유형이거나 더 많은 유형이 인 파생 형식을 사용할 수 있습니다. 공분산 및 반항에 대한 자세한 내용은 공진 및 공분산 제네릭을 참조하십시오.

나는 그렇다면, 내가 다른 인터페이스 측면에서 순전히 IContainer 인터페이스를 지정하기보다는 구체적인 클래스를 사용하여 내 목표를 달성 할 수있는 방법이 &, 수정 암?

아니면 더 근본적인 것을 오해하고 있습니까?

답변

5

.NET 4의 일반 분산은 여기에 관계가 없습니다. 인터페이스의 구현은 형식 측면에서 정확히의 인터페이스 서명과 일치해야합니다.

public interface ICloneable 
{ 
    object Clone(); 
} 

좋은이처럼 구현할 수 있도록 다음과 같습니다 :

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

을 ...하지만 예를 들어

은 다음과 같습니다하는 ICloneable을.NET은 이것을 허용하지 않습니다. 당신은 가끔과 같이,이 문제를 해결 명시 적 인터페이스 구현 작업을 사용할 수 있습니다

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

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

그러나 귀하의 경우에 당신이 이제까지 그렇게 할 수 없습니다. ConcreteContainerIContainer을 구현하기 위해 고려 된 경우 유효 할 것이다 다음 코드를 살펴 보자 :

이 명확 와 함께 작동 할 수 있도록 이제 속성 setter는 사실 만 EntityCollection<ConcreteChild> 작업을 선언
IContainer foo = new ConcreteContainer(); 
foo.Children = new List<IChild>(); 

어떤IEnumerable<IChild> - 인터페이스를 위반합니다.

4

내가 이해하는 한, 당신은 인터페이스를 구현해야한다 - 당신은 공분산/반대 변형 멤버가 대체물로 선택 될 것이라고 생각할 수 없다. 허용되는 경우에도 어린이를위한 세터가 문제가됨을 유의하십시오. EntityCollection<ConcreteChild>의 속성을 List<ConcreteChild> 또는 EntityCollection<ConcreteChild2>과 같은 다른 형식의 값으로 설정할 수 있으므로 모두 IEnumerable<IChild>을 구현합니다.

현재 디자인에서는 IContainer를 개인적으로 ConcreteContainer에 구현하고 IEnumerable.Children setter의 입력 값을 호환 유형으로 확인합니다. 이 디자인에 접근하는 또 다른 방법은 다음과 같은 제네릭 인터페이스를 사용하는 것입니다.

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

내 의혹을 확인해 주셔서 감사합니다. 일반 인터페이스 접근법은 훌륭하지만 EF4에서는 작동하지 않습니다. EF4는 단위 테스트를 할 때 EF4를 매우 조롱하게 만듭니다. 결과적으로, 우리는 주로 EF 개체에 대한 통합 테스트로 이동했습니다. –

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; 
     } 
    } 
} 
관련 문제