1

是短:

为什么将派生类型添加到集合会通过,但是在尝试添加派生类型的泛型时会失败?

“短”代码:

//a generic repository
        public class EfRepository<T> : IRepository<T> where T: BaseCatalogModel{...}


        public CatalogRepository(IRepository<Product> productRepository, IRepository<Category> categoryRepository)
    {
        //This passes
        Dictionary<int, BaseCatalogModel> dic1 = new Dictionary<int, BaseCatalogModel>();
        dic1.Add(1, new Product());
        dic1.Add(2, new Category());
        dic1.Add(3, new BaseCatalogModel());


        //This not. 
        //The error: cannot convert from 'YoYo.Core.Data.Repositories.EfRepository<YoYo.Commerce.Common.Domain.Catalog.Product>' 
        //to 'YoYo.Core.Data.Repositories.EfRepository<YoYo.Commerce.Common.Domain.Catalog.BaseCatalogModel>'
        Dictionary<int, EfRepository<BaseCatalogModel>> dic2 = new Dictionary<int, EfRepository<BaseCatalogModel>>();
        dic2.Add(1, new EfRepository<Product>());
        dic2.Add(2, new EfRepository<Category>());
    }

长期交易:在在线商店工作,我想在目录存储库中保存与管理目录相关的所有存储库的集合。

这个想法是从一个存储库管理整个目录。

存储库集合是字典类型)

我无法将任何 BaseCatalogModel 派生类型存储库添加到集合中。

我很乐意在上述方面获得任何帮助或获得更好实施的建议。

public class BaseCatalogModel
{
    public int Id { get; set; }
    ...
}
public class Category:BaseCatalogModel
{
    ...
}
public class Product : BaseCatalogModel
{
    ...
}


public class CatalogRepository : ICatalogRepository 
{
    private readonly Dictionary<Type, IRepository<BaseEntity>> _repositoriesCollection= new Dictionary<Type, IRepository<BaseEntity>>();
    public CatalogRepository(IRepository<Product> productRepository, IRepository<Category> categoryRepository)
    {
        _repositoriesCollection.Add(typeof(Category), categoryRepository); //==> this fails 
        _repositoriesCollection.Add(typeof(Product), productRepository);    //==> this fails
    }


    public T GetCatalogItem<T>(int id) where T : BaseCatalogModel
    {
        //returns a catalog item using type and id
    }

    public IEnumerable<T> GetCatalogItem<T>() where T : BaseCatalogModel
    {
        //returns the entire collection of catalog item
    }
}
4

1 回答 1

1

所以这是泛型的一个相当普遍的问题。

想象 2 个班级class Baseclass A. class ABase.

    public class Base { }

    public class A : Base { }

现在考虑List<T>。您可以从List<T>持有 Base 或 A 的类开始:

List<Base> x = new List<Base>();

List<A> y = new List<A>();

一个常见的误解是 y 的类必须是 x 的类的后代,但这不可能是真的,因为 x 有一个类似的方法Add(Base item),而 y 有一个类似的方法Add(A item),编译器不可能保证在 y该接口将与 x 的接口兼容。这是因为如果您将一个实例List<A>视为一个实例,List<Base>则没有什么可以阻止使用 Base 的实例或 Base 的另一个子类调用 Add。

现在可以保证接口的某些部分是兼容的。它们是返回类 A 实例的任何部分,因为 A 总是可以代替 Base。

如果您的界面仅输出泛型并且您使用的是 .net 4,则有一个简单的解决方案。out 通用修饰符:

    public class Example
    {

        private readonly Dictionary<Type, IRepository<Base>> _repositoriesCollection =
            new Dictionary<Type, IRepository<Base>>();

        public void DoSomething()
        {
            _repositoriesCollection.Add(typeof(A), new Repository<A>());
        }
    }


    interface IRepository<out T> where T : Base
    {
        T MakeSomeItem(string info);
        //void AddSomeItem(string info, T itemToAdd); <- this will not 
                                                        // work because T
                                                        // is out - so can't 
                                                        // go in... 
        IEnumerable<T> MakeSomeListOfItems(); // This is OK because 
             // IEnumerable<T> is declared as IEnumerable<out T> in the fx

        //List<T> Something(); <- not ok because List<T> is not List<out T>
    }

    public class Repository<T> : IRepository<T> where T : Base
    {
        public T MakeSomeItem(string info)
        {
            throw new NotImplementedException();
        }

        public IEnumerable<T> MakeSomeListOfItems()
        {
           throw new NotImplementedException();
        }
    }

    public class Base { }

    public class A : Base { }

此解决方案不适用于 2 种情况;当您需要将项目传递到您的界面以及不使用 .net 4 时。

这两种情况也有许多不同的解决方案。

1)我也需要将一个项目传递到接口中,并且我正在使用.net 4 - 只需将其作为 Base 传递,如果您需要维护类型安全,请在其他地方使用通用方法将其包装起来。

    interface IRepository<out T> where T : Base
    {
        T MakeSomeItem(string info);
        void AddSomeItem(string info, Base itemToAdd);
    }

    public class Repository<T> : IRepository<T> where T : Base
    {
        public T MakeSomeItem(string info){ throw new NotImplementedException(); }

        public void AddSomeItem(string info, Base itemToAdd)
        {
            T castedItem = (T) itemToAdd; //fails here at 
                                          //run time if not 
                                          // correct type
            AddSomeItem(info, itemToAdd);
        }

        public void AddSomeItem(string info, T itemToAdd)
        {
            /// do it for real...
        }
    }

2) 如果您不使用 .net 4,那么您还可以执行其他操作,强制存储库实现您的接口的 Base 版本:

interface IRepository<T> where T : Base
{
    T MakeSomeItem(string info);
    void AddSomeItem(string info, T itemToAdd)
}

public class Repository<T> : IRepository<Base>, IRepository<T> where T : Base
{
    public T MakeSomeItem(string info) { throw new NotImplementedException(); }

    public void AddSomeItem(string info, Base itemToAdd)
    {
        T castedItem = (T) itemToAdd; //fails here at 
                                      //run time if not 
                                      // correct type
        AddSomeItem(info, itemToAdd);
    }

    public void AddSomeItem(string info, T itemToAdd)
    {
        /// do it for real...
    }

    Base IRepository<Base>.MakeSomeItem(string info)
    {
        return MakeSomeItem(info);
    }
}

如果你想保持你的输入是强类型的,你还可以做更多的事情——但我认为我的回答现在已经足够长了。

于 2012-07-28T02:27:00.227 回答