C#-无法将类型List <Product>隐式转换为List <IProduct>


89

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

我已经做过数百次了,但是由于某种原因我现在遇到了这个错误:

无法将类型'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
    }
}

在另一堂课中,我有:

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

任何想法为什么会发生这种情况?(而且我敢肯定这很简单!)

Answers:


113

是的,这是C#中的协方差限制。您不能将一种类型的列表转换为另一种类型的列表。

代替:

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

(以及为什么它不同于处理项目数组的原因)。


丹尼尔和凯文都以不同的方式回答了这个问题。太棒了!对/协方差的合理解释。的确是IN OUT,但不是两者。没有使用过.NET 4.0,这让我完全没想到!多谢你们。
基思·巴罗斯

39

你不能那样做。如果您有List<IProduct>,则可以在其中放入任何 IProduct内容。因此,如果您有Product2实现的工具IProduct,则可以将其放在列表中。但是原始列表的创建方式为List<Product>,因此使用该列表的任何人都希望仅类型为的对象Product不在Product2列表中。

在.NET 4.0中,他们为接口添加了协方差和协方差,因此您可以转换IEnumerable<Product>IEnumerable<IProduct>。但这仍然不适用于列表,因为列表界面允许您“放入东西”和“取出东西”。


8
关于能够使用IEnumerable的不错提示
Lee Englestone

我对此部分还是有些困惑:>因此,使用列表的任何人都希望列表中仅包含Product类型的对象,而不是Product2类型的对象。用户为什么要做出这个假设?如果我使用声明为List <IProduct>的东西,则不会期望能够自动将元素转换为一个实现或另一个实现。(这可能是MSFT我不喜欢的古怪选择,但如果不是这样,我真的想尝试理解他们的理由。)
埃莉诺·霍利


4

好吧,你可以用这个!

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

2

这是一个小例子。

    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

尝试以下方法:

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

7
认真吗?你知道那意味着什么吗?
Nix
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.