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!)

Keith Barrows
quelle

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

kemiller2002
quelle
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.

Daniel Plaisted
quelle
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
5

Nur als Bemerkung: Kovarianz und Kontravarianz in Generika wurden in C # 4.0 hinzugefügt.

Danvil
quelle
1
Wird jedoch in Konstrukten mit IN- und OUT-Kanälen nicht unterstützt. List <T> ist ein solches Konstrukt, daher unterstützen sie keine Kontra / Kovarianz!
Keith Barrows
Ja, es würde nur funktionieren, wenn ein IEnumerable <contracts.IProduct>
Danvil
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());
}
Nayan
quelle
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.  ");
        }
    }
Jimmy
quelle
-3

Versuchen Sie stattdessen Folgendes:

List<contracts.IProduct> myList = new List<contracts.IProduct>((new List<dto.Product>()).Cast<contracts.IProduct>());
Hopfenreich
quelle
7
Ernsthaft? Weißt du was das überhaupt bedeutet?
Nix