crichavin
crichavin

Reputation: 4582

Generic Parameters within Generic Class

I am trying to create a Generic Task layer (referred to as AppServ in my project) to interact with my Generic Repository. This is my first dive into generics and am having issues with passing a value to a generic method parameter that is within a Generic Class:

The Base App Services class (which is inherited by a specific app serv class and calls the Generic Repository):

public class BaseAppServ<T> : IBaseAppServ<T> where T : class, IEntity, IAuditStamps, ICompanyFacility, new()
{
    private Repository<T> _repository;
    private T _viewModel;
    private AuditStampsViewModel _auditStamps;

    public BaseAppServ(Repository<T> repository)
    {
        _repository = repository;
        _viewModel = new T();
        _auditStamps = new AuditStampsViewModel();
    }

Here is my specific AppServ class (inherits the BaseAppServ)

public class ManageItemsAppServ : BaseAppServ<ManageItemsAppServ>, IEntity, IAuditStamps, ICompanyFacility
    {

        public ManageItemsAppServ()
            : base(CallBaseConstructor())
            {

            }

            public static Repository<Item> CallBaseConstructor()
            {
                return new Repository<Item>(new InventoryMgmtContext());
            }

I am having issues in the constructor or ManageItemsAppServ, specifically in the base(CallBaseConstructor()) line. It gives an error of:

Argument 1: cannot convert from 'OTIS.Data.Repository<OTIS.domain.InventoryMgmt.Item>' to 'OTIS.Data.Repository<OTIS.AppServ.InventoryMgmt.ManageItemsAppServ>'

I think I know why this is happening (because when I inherit the BaseAppServ I am specifying T of type ManageItemsAppServ and that sets the type of T for the entire BaseAppServ class....right?)

So how do I redefine T for the constructor call which is looking for type Respository?

EDIT: So I think I need to add a second generic parameter in the BaseAppServ class like (note the U which I constrain to type Repository):

public class BaseAppServ<T, U> where U : Repository<U>, IBaseAppServ<T> where T : class, IEntity, IAuditStamps, ICompanyFacility, new()
    {
        private Repository<U> _repository;
        private T _viewModel;
        private AuditStampsViewModel _auditStamps;

        public BaseAppServ(Repository<U> repository)
        {
            _repository = repository;
            _viewModel = new T();
            _auditStamps = new AuditStampsViewModel();
        }

This seems to be the right path and now the only error is:

Inconsistent accessibility: constraint type 'OTIS.AppServ.IBaseAppServ<T>' is less accessible than 'OTIS.AppServ.BaseAppServ<T,U>'

Which has something to do with the order/syntax of the BaseAppServ class declaration. What should it be?

Upvotes: 1

Views: 209

Answers (1)

Lucero
Lucero

Reputation: 60276

You're trying to pass a Repository<Item> as constructor argument where a Repository<ManageItemsAppServ> is expected.

Note that even if Item inherits from ManageItemsAppServ this is not a valid operation, because generic classes cannot be co- or contravariant.

So, in short, make sure to pass in the exact type, or to use an interface that is covariant (whether the interface can be covariant depends on the methods on it).

Edit:

As per your edit, you probaly want something like this:

public class BaseAppServ<TModel, TItem>: IBaseAppServ<TModel>
            where TItem : class, IEntity 
            where TModel: ICompanyFacility, new()
{
    private Repository<TItem> _repository;
    private TModel _viewModel;
    private AuditStampsViewModel _auditStamps;

    public BaseAppServ(Repository<TItem> repository)
    {
        _repository = repository;
        _viewModel = new TModel();
        _auditStamps = new AuditStampsViewModel();
    }

Upvotes: 1

Related Questions