C # - Typ List <Produkt> kann nicht implizit in Liste <IProdukt> konvertiert werden


89

Ich habe ein Projekt mit all meinen Schnittstellendefinitionen: RivWorks.Interfaces
Ich habe ein Projekt, in dem ich konkrete Implementierungen definiere: RivWorks.DTO

Ich habe das schon hunderte Male gemacht, aber aus irgendeinem Grund erhalte ich jetzt diesen Fehler:

Typ 'System.Collections.Generic.List <RivWorks.DTO.Product>' kann nicht implizit in 'System.Collections.Generic.List <RivWorks.Interfaces.DataContracts.IProduct>' konvertiert werden.

Schnittstellendefinition (verkürzt):

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

Konkrete Klassendefinition (verkürzt):

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

In einer anderen Klasse habe ich:

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

Irgendwelche Ideen, warum dies passieren könnte? (Und ich bin sicher, es ist etwas trivial Einfaches!)

Antworten:


113

Ja, es ist eine Kovarianzbeschränkung in C #. Sie können eine Liste eines Typs nicht in eine Liste eines anderen Typs konvertieren.

Anstatt:

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

Das musst du machen

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

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

Eric Lippert erklärt, warum sie es so implementiert haben: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

(Und warum ist es anders als mit Arrays von Elementen zu arbeiten).


Sowohl Daniel als auch Kevin antworteten darauf, allerdings auf unterschiedliche Weise. Bravo! Faire Erklärung von Kontra / Kovarianz. Es ist zwar entweder IN oder OUT, aber nicht beides. Nachdem ich .NET 4.0 nicht verwendet habe, ist mir das völlig durch den Kopf gegangen! Danke Leute.
Keith Barrows

39

Das kannst du nicht machen. Wenn Sie eine haben List<IProduct>, können Sie jede IProduct hineinstecken. Wenn Sie also ein Product2Gerät haben, können IProductSie es in die Liste aufnehmen. Die ursprüngliche Liste wurde jedoch als erstellt List<Product>, sodass jeder, der die Liste verwendet, nur Objekte vom Typ erwartet Product, die nicht Product2in der Liste enthalten sind.

In .NET 4.0, fügten sie Kovarianz und Kontra für Schnittstellen, so dass Sie konvertieren könnte IEnumerable<Product>zu IEnumerable<IProduct>. Dies funktioniert jedoch immer noch nicht für Listen, da Sie über die Listenoberfläche sowohl "Sachen einfügen" als auch "Sachen rausholen" können.


8
Netter Tipp über die Verwendung von IEnumerable
Lee Englestone

Ich bin immer noch ein wenig verwirrt von diesem Teil:> Jeder, der die Liste verwendet, würde erwarten, dass nur Objekte vom Typ Produkt und nicht Produkt2 in der Liste enthalten sind. Warum sollte der Benutzer diese Annahme machen? Wenn ich etwas verwende , das als List <IProduct> deklariert ist, würde ich nicht erwarten, dass Elemente automatisch auf die eine oder andere Implementierung heruntergestuft werden können. (Es ist möglich, dass dies nur eine eigenartige Wahl ist, die ich von MSFT nicht mag, aber wenn nicht, möchte ich wirklich versuchen, ihre Argumentation zu verstehen.)
Eleanor Holley


4

Nun, Sie können dies verwenden!

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

Nun, um direkte Lösung zu geben,

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

2

Dies ist ein kleines Beispiel dafür.

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

-3

Versuchen Sie stattdessen Folgendes:

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

7
Ernsthaft? Weißt du was das überhaupt bedeutet?
Nix
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.