C# – 不能隐式地将List <Product>转换为List <IProduct>

我有我所有的接口定义项目:RivWorks.Interfaces
我有一个项目,我定义了具体的实践:RivWorks.DTO

我已经做了几百次之前,但由于某种原因,我现在得到这个错误:

不能将types'System.Collections.Generic.List <RivWorks.DTO.Product>'隐式转换为'System.Collections.Generic.List <RivWorks.Interfaces.DataContracts.IProduct>'

接口定义(缩写):

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

具体类定义(缩写):

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

在另一个class上我有:

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

任何想法,为什么这可能会发生? (我相信这是简单的事情!)

是的,这是C#中的协变限制。 您不能将一种types的列表转换为另一种types的列表。

代替:

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

你必须这样做

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

Eric Lippert解释了为什么他们这样实现它: http : //blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

(为什么它不同于处理项目数组)。

你不能这样做。 如果你有一个List<IProduct> ,你可以放入任何 IProduct 。 所以如果你有一个实现IProduct你可以把它放在列表中。 但是最初的列表是以List<Product>创build的,所以任何使用这个列表的人都只会期望Producttypes的对象,而不是Product2

在.NET 4.0中,他们为接口添加了协变和反转,因此您可以将IEnumerable<Product>转换为IEnumerable<IProduct> 。 但是对于列表来说,这仍然不起作用,因为列表界面允许你们“把东西放进去”和“把东西弄出去”。

正如一句话:在C#4.0中添加了generics中的协变和逆变 。

那么,你可以使用这个!

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

现在,要直接解决,

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

这是一个小例子如何做到这一点。

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

试试这个:

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