Search code examples
c#genericsgeneric-programming

Generic type of a generic type?


I have the following code :

public abstract class ListPresenter<TView, TModel, TEntity> : Presenter<TView, TModel>
    where TView : IListView<TModel>
    where TModel : EntityListViewModel<TEntity>
    where TEntity : class, IEntity
{
    public ListPresenter(TView view, TModel model)
        : base(view, model)
    {
    }
}

Is there any way to avoid the third parameter? If I try to use :

public abstract class ListPresenter<TView, TModel> : Presenter<TView, TModel>
    where TView : IListView<TModel>
    where TModel : EntityListViewModel<IEntity>

I get:

Error 3 The type 'PuntoVenta.ViewModels.SaleListViewModel' cannot be used as type parameter 'TModel' in the generic type or method 'PuntoVenta.Presentation.ListPresenter'. There is no implicit reference conversion from 'PuntoVenta.ViewModels.SaleListViewModel' to 'PuntoVenta.ViewModels.EntityListViewModel'. C:\Users\Marc\Dropbox\Source\PointOfSale\PuntoVenta\Presentation\ListSalePresenter.cs 26 20 PuntoVenta.UI

even EntityListViewModel< IEntity > will always be true.

public abstract class EntityListViewModel<TEntity> : ViewModelBase, IEntityListViewModel<TEntity>
    where TEntity : class, IEntity 
{
    private BindingSource Entities { get; set; }

    private string searchQuery = string.Empty;

    public EntityListViewModel()
    {
        SearchQuery = string.Empty;
        Entities = new BindingSource();
    }

    public TEntity Selected
    {
        get { return Entities.Current as TEntity; }
        set { Entities.Position = Entities.IndexOf(value); }
    }

    public string SearchQuery
    {
        get { return searchQuery; }
        set
        {
            searchQuery = value;
            NotifyProperty("SearchQuery");
        }
    }

    public List<TEntity> DataSource
    {
        set
        {
            Entities.DataSource = value;
            NotifyProperty("DataSource");
        }
    }
}

public interface IEntity
{
    Guid Id { get; set; }

    DateTime DateCreated { get; set; }
    DateTime DateUpdated { get; set; }
}

public interface IListView<TModel> : IView<TModel>
    where TModel : ViewModelBase
{
    event EventHandler OnSearchQueryChanged;
    event EventHandler OnSelectRequested;
}

public abstract class ViewModelBase : IModel
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyProperty(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class SaleListViewModel : EntityListViewModel<SaleView>
{
    private bool _ShowPendingOnly = false;

    public SaleListViewModel()
    {
    }

    public bool ShowPendingOnly
    {
        get { return _ShowPendingOnly; }
        set
        {
            _ShowPendingOnly = value;
            NotifyProperty("ShowPendingOnly");
        }
    }

    public List<Customer> Customers { get; set; }
}

Solution

  • Avoid to use generic type on declaration of EntityListViewModel.

    There is no need of declare with generics in this case whether the objects always implement IEntity interface.

    public abstract class EntityListViewModel
                          : ViewModelBase, IEntityListViewModel<IEntity>
    

    You also need to change any reference in this class to TEntity.

    public IEntity Selected
    {
        get { return Entities.Current as IEntity; }
        set { Entities.Position = Entities.IndexOf(value); }
    }
    
    public List<IEntity> DataSource
    {
        set
        {
            Entities.DataSource = value;
            NotifyProperty("DataSource");
        }
    }
    

    At this point, you can declare ListPresenter as

    public abstract class ListPresenter<TView, TModel>
                          : Presenter<TView, TModel>
                                     where TView : IListView<TModel>
                                     where TModel : EntityListViewModel<IEntity>