存储库集合

时间:2012-07-27 23:03:19

标签: c# generics collections

简短:

为什么要将派生类型添加到集合中,但是当尝试添加派生类型的泛型时,它会失败?

“简短”代码:

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

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

我们的想法是从一个存储库管理整个目录。

存储库集合的类型为Dictionary)

我无法将任何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
    }
}

1 个答案:

答案 0 :(得分:1)

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

想象2个班class Baseclass Aclass A来自Base

    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)之类的方法并且编译器不可能保证接口将与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);
    }
}

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