C# - Cannot implicitly convert type List<Product> to List<IProduct>
I have a project with all my Interface definitions: RivWorks.Interfaces
I have a project where I define concrete implmentations: RivWorks.DTOI've done this hundreds of times before but for some reason I am getting this error now:
Cannot implicitly convert type 'System.Collections.Generic.List<RivWorks.DTO.Product>' to 'System.Collections.Generic.List<RivWorks.Interfaces.DataContracts.IProduct>'
Interface definition (shortened):
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; }
...
}
}
Concrete class definition (shortened):
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 = pr开发者_开发技巧oduct.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 another class I have:
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>();
...
Any ideas why this might be happening? (And I am sure it is something trivially simple!)
Yep it's a covariance limitation in C#. You can't convert a list of one type to a list of another.
Instead of:
List<contracts.IProduct> myList = new List<dto.Product>();
You have to do this
List<contracts.IProduct> myList = new List<contracts.IProduct>();
myList.Add(new dto.Product());
Eric Lippert explains why they implemented it this way: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx (archived)
(And why it is different than working with arrays of items).
You can't do that. If you have a List<IProduct>
, you can put any IProduct
in it. So if you have a Product2
which implements IProduct
you could put it in the list. But the original list was created as List<Product>
, so anyone using the list would expect only objects of type Product
, not Product2
to be in the list.
In .NET 4.0, they added covariance and contravariance for interfaces, so you could convert IEnumerable<Product>
to IEnumerable<IProduct>
. But this still doesn't work for lists, since the list interface allows you both to "put stuff in" and "get stuff out".
Just as a remark: Covariance and Contravariance in Generics was added in C# 4.0.
Well, you can use this!
class A {}
class B : A {}
...
List<B> b = new List<B>();
...
List<A> a = new List<A>(b.ToArray());
Now, to give direct solution,
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());
}
This is a little example how to do it.
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. ");
}
}
Try this instead:
List<contracts.IProduct> myList = new List<contracts.IProduct>((new List<dto.Product>()).Cast<contracts.IProduct>());
精彩评论