web-dev-qa-db-fra.com

C # - Impossible de convertir implicitement le type List <Product> en List <IProduct>

J'ai un projet avec toutes mes définitions d'interface: RivWorks.Interfaces
J'ai un projet où je définis des implémentations concrètes: RivWorks.DTO

Je l'ai fait des centaines de fois auparavant, mais pour une raison quelconque, je reçois cette erreur maintenant:

Impossible de convertir implicitement le type 'System.Collections.Generic.List <RivWorks.DTO.Product>' en 'System.Collections.Generic.List <RivWorks.Interfaces.DataContracts.IProduct>'

Définition d'interface (raccourcie):

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

Définition de classe concrète (raccourcie):

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

Dans une autre classe, j'ai:

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>();
    ...

Des idées pourquoi cela pourrait se produire? (Et je suis sûr que c'est quelque chose de très simple!)

81
Keith Barrows

Oui, c'est une limitation de covariance en C #. Vous ne pouvez pas convertir une liste d'un type en liste d'un autre.

Au lieu de:

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

Tu dois faire ca

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

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

Eric Lippert explique pourquoi ils l'ont implémenté de cette façon: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

(Et pourquoi c'est différent de travailler avec des tableaux d'articles).

102
kemiller2002

Tu ne peux pas faire ça. Si tu as un List<IProduct>, vous pouvez y mettre anyIProduct. Donc, si vous avez un Product2 qui implémente IProduct vous pouvez le mettre dans la liste. Mais la liste d'origine a été créée sous la forme List<Product>, donc toute personne utilisant la liste ne s'attendrait qu'à des objets de type Product, pas Product2 pour figurer dans la liste.

Dans .NET 4.0, ils ont ajouté la covariance et la contravariance pour les interfaces, afin que vous puissiez convertir IEnumerable<Product> à IEnumerable<IProduct>. Mais cela ne fonctionne toujours pas pour les listes, car l'interface de liste vous permet à la fois de "mettre des choses dedans" et de "sortir des choses".

37
Daniel Plaisted

Juste une remarque: Covariance et Contravariance dans les génériques a été ajouté en C # 4.0.

5
Danvil

Eh bien, vous pouvez l'utiliser!

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

Maintenant, pour donner une solution directe,

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());
}
4
Nayan

Ceci est un petit exemple comment le faire.

    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.  ");
        }
    }
2
Jimmy