2010-04-14 34 views
66

Tengo un proyecto con todos mis definiciones de interfaz: RivWorks.Interfaces
tengo un proyecto en el que defino implmentations concretas: RivWorks.DTOC# - No se puede convertir implícitamente el tipo de lista <Product> a la lista <IProduct>

que he hecho esto cientos de veces antes, pero por alguna razón estoy recibiendo este error ahora:

no se puede convertir implícitamente el tipo 'System.Collections.Generic.List <RivWorks.DTO.Product>' a 'System.Collections.Generic. Lista < RivWorks.Int erfaces.DataContracts.IProduct > '

definición de interfaz (abreviado):

namespace RivWorks.Interfaces.DataContracts 
{ 
    public interface IProduct 
    { 
     [XmlElement] 
     [DataMember(Name = "ID", Order = 0)] 
     Guid ProductID { get; set; } 
     [XmlElement] 
     [DataMember(Name = "altID", Order = 1)] 
     long alternateProductID { get; set; } 
     [XmlElement] 
     [DataMember(Name = "CompanyId", Order = 2)] 
     Guid CompanyId { get; set; } 
     ... 
    } 
} 

definición de clase de hormigón (abreviado):

namespace RivWorks.DTO 
{ 
    [DataContract(Name = "Product", Namespace = "http://rivworks.com/DataContracts/2009/01/15")] 
    public class Product : IProduct 
    { 
     #region Constructors 
     public Product() { } 
     public Product(Guid ProductID) 
     { 
      Initialize(ProductID); 
     } 
     public Product(string SKU, Guid CompanyID) 
     { 
      using (RivEntities _dbRiv = new RivWorksStore(stores.RivConnString).NegotiationEntities()) 
      { 
       model.Product rivProduct = _dbRiv.Product.Where(a => a.SKU == SKU && a.Company.CompanyId == CompanyID).FirstOrDefault(); 
       if (rivProduct != null) 
        Initialize(rivProduct.ProductId); 
      } 
     } 
     #endregion 

     #region Private Methods 
     private void Initialize(Guid ProductID) 
     { 
      using (RivEntities _dbRiv = new RivWorksStore(stores.RivConnString).NegotiationEntities()) 
      { 
       var localProduct = _dbRiv.Product.Include("Company").Where(a => a.ProductId == ProductID).FirstOrDefault(); 
       if (localProduct != null) 
       { 
        var companyDetails = _dbRiv.vwCompanyDetails.Where(a => a.CompanyId == localProduct.Company.CompanyId).FirstOrDefault(); 
        if (companyDetails != null) 
        { 
         if (localProduct.alternateProductID != null && localProduct.alternateProductID > 0) 
         { 
          using (FeedsEntities _dbFeed = new FeedStoreReadOnly(stores.FeedConnString).ReadOnlyEntities()) 
          { 
           var feedProduct = _dbFeed.AutoWithImage.Where(a => a.ClientID == companyDetails.ClientID && a.AutoID == localProduct.alternateProductID).FirstOrDefault(); 
           if (companyDetails.useZeroGspPath.Value || feedProduct.GuaranteedSalePrice > 0)  // kab: 2010.04.07 - new rules... 
            PopulateProduct(feedProduct, localProduct, companyDetails); 
          } 
         } 
         else 
         { 
          if (companyDetails.useZeroGspPath.Value || localProduct.LowestPrice > 0)    // kab: 2010.04.07 - new rules... 
           PopulateProduct(localProduct, companyDetails); 
         } 
        } 
       } 
      } 
     } 
     private void PopulateProduct(RivWorks.Model.Entities.Product product, RivWorks.Model.Entities.vwCompanyDetails RivCompany) 
     { 
      this.ProductID = product.ProductId; 
      if (product.alternateProductID != null) 
       this.alternateProductID = product.alternateProductID.Value; 
      this.BackgroundColor = product.BackgroundColor; 
      ... 
     } 
     private void PopulateProduct(RivWorks.Model.Entities.AutoWithImage feedProduct, RivWorks.Model.Entities.Product rivProduct, RivWorks.Model.Entities.vwCompanyDetails RivCompany) 
     { 
      this.alternateProductID = feedProduct.AutoID; 
      this.BackgroundColor = Helpers.Product.GetCorrectValue(RivCompany.defaultBackgroundColor, rivProduct.BackgroundColor); 
      ... 
     } 
     #endregion 

     #region IProduct Members 
     public Guid ProductID { get; set; } 
     public long alternateProductID { get; set; } 
     public Guid CompanyId { get; set; } 
     ... 
     #endregion 
    } 
} 

En otra clase que tengo:

using dto = RivWorks.DTO; 
using contracts = RivWorks.Interfaces.DataContracts; 
... 
public static List<contracts.IProduct> Get(Guid companyID) 
{ 
    List<contracts.IProduct> myList = new List<dto.Product>(); 
    ... 

Alguna idea de por qué esto podría estar sucediendo? (¡Y estoy seguro de que es algo trivialmente simple!)

Respuesta

86

Sí, es una limitación de covarianza en C#. No puede convertir una lista de un tipo en una lista de otro.

En lugar de:

List<contracts.IProduct> myList = new List<dto.Product>(); 

Tienes que hacer esto

List<contracts.IProduct> myList = new List<contracts.IProduct>(); 

myList.Add(new dto.Product()); 

Eric Lippert explica por qué se implementan esta manera: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

(y por qué es diferente a trabajar con arreglos de elementos).

+0

Tanto Daniel y Kevin respondió esto, aunque de maneras diferentes. ¡Bravo! Explicación justa de contra/covarianza. De hecho, es IN ** o ** OUT pero no ambos. ¡No haber usado .NET 4.0 que se me olvidó por completo! Gracias chicos. –

26

No puede hacer eso. Si tiene un List<IProduct>, puede poner cualquierIProduct en él. Entonces, si tiene un Product2 que implementa IProduct, puede ponerlo en la lista. Pero la lista original se creó como List<Product>, por lo que cualquiera que use la lista esperaría que solo los objetos del tipo Product, no Product2 estén en la lista.

En .NET 4.0, agregaron covarianza y contravarianza para las interfaces, por lo que podría convertir IEnumerable<Product> en IEnumerable<IProduct>. Pero esto todavía no funciona para las listas, ya que la interfaz de la lista les permite a ambos "poner cosas" y "sacar cosas".

+3

Buen consejo acerca de poder usar IEnumerable –

-3

Tal vez puedas probar:

List<contracts.IProduct> myList = new List<contracts.IProduct>((new List<dto.Product>()).Cast<contracts.IProduct>()); 
+7

en serio? ¿Sabes lo que eso significa? – Nix

2

Bueno, se puede utilizar esta!

 class A {} 
     class B : A {} 
     ... 
     List<B> b = new List<B>(); 
     ... 
     List<A> a = new List<A>(b.ToArray()); 

Ahora, para dar una solución directa,

using dto = RivWorks.DTO; 
using contracts = RivWorks.Interfaces.DataContracts; 
... 
public static List<contracts.IProduct> Get(Guid companyID) { 
    List<dto.Product> prodList = new List<dto.Product>(); 
    ... 
    return new List<contracts.IProduct>(prodList.ToArray()); 
} 
5

Así como una observación: se añadió Covariance and Contravariance in Generics en C# 4.0.

+1

Pero no es compatible con construcciones que tengan canales IN y OUT. La lista es una construcción así que no admiten contra/covarianza. –

+0

Sí, solo funcionaría si se utiliza un IEnumerable Danvil

1

Este es un pequeño ejemplo de cómo hacerlo.

public void CreateTallPeople() 
    { 
     var tallPeopleList = new List<IPerson> 
     { 
      new TallPerson {Height = 210, Name = "Stevo"}, 
      new TallPerson {Height = 211, Name = "Johno"}, 
     }; 
     InteratePeople(tallPeopleList); 
    } 

    public void InteratePeople(List<IPerson> people) 
    { 
     foreach (var person in people) 
     { 
      Console.WriteLine($"{person.Name} is {person.Height}cm tall. "); 
     } 
    } 
Cuestiones relacionadas