【问题标题】:C# - Cannot implicitly convert type List<Product> to List<IProduct>C# - 无法将类型 List<Product> 隐式转换为 List<IProduct>
【发布时间】:2011-02-08 02:29:32
【问题描述】:

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

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

无法将类型“System.Collections.Generic.List”隐式转换为“System.Collections.Generic.List

接口定义(缩短):

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

任何想法为什么会发生这种情况? (而且我确信这非常简单!)

【问题讨论】:

    标签: c# interface compiler-errors


    【解决方案1】:

    是的,这是 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,这让我完全忘记了!谢谢大家。
    【解决方案2】:

    你不能那样做。如果你有一个List&lt;IProduct&gt;,你可以把 any IProduct 放进去。所以如果你有一个实现IProductProduct2,你可以把它放在列表中。但原始列表创建为List&lt;Product&gt;,因此使用该列表的任何人都希望列表中只有Product 类型的对象,而不是Product2

    在 .NET 4.0 中,他们为接口添加了协变和逆变,因此您可以将 IEnumerable&lt;Product&gt; 转换为 IEnumerable&lt;IProduct&gt;。但这仍然不适用于列表,因为列表界面允许您“放入”和“取出”。

    【讨论】:

    • 关于能够使用 IEnumerable 的好建议
    • 这部分我还是有点困惑:> 所以使用列表的任何人都希望列表中只有 Product 类型的对象,而不是 Product2。为什么用户会做出这样的假设?如果我使用 declared 作为 List 的东西,我不希望能够自动将元素向下转换为一个实现或另一个实现。 (这可能只是我不喜欢 MSFT 的一个古怪选择,但如果不是,我真的想尝试理解他们的推理。)
    【解决方案3】:

    试试这个:

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

    【讨论】:

    • 认真的吗?你知道这意味着什么吗?
    【解决方案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());
    }
    

    【讨论】:

      【解决方案5】:

      正如备注:Covariance and Contravariance in Generics 是在 C# 4.0 中添加的。

      【讨论】:

      • 但在同时具有 IN 和 OUT 通道的结构中不受支持。 List 就是这样的构造,因此它们不支持反/协方差!
      • 是的,它只有在使用 IEnumerable 时才有效
      【解决方案6】:

      这是一个小例子。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-01
        • 2013-07-24
        • 2014-01-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多