26

Say I have the following interface for exposing a paged list

public interface IPagedList<T>
{
    IEnumerable<T> PageResults { get; }
    int CurrentPageIndex { get; }
    int TotalRecordCount { get; }
    int TotalPageCount { get; }        
    int PageSize { get; }
}   

Now I want to create a paging control

public class PagedListPager<T>
{
    public PagedListPager<T>(IPagedList<T> list)
    {
        _list = list;
    }

    public void RenderPager()
    {
        for (int i = 1; i < list.TotalPageCount; i++)
            RenderLink(i);
    }
}

The paging control has no interest in T (the actual contents of the list). It only requires the number of pages, current page etc. So the only reason PagedListPager is generic is so that it will compile with the generic IPagedList<T> paramater.

Is this a code smell? Should I care that I effectively have a redundant generic?

Is there a standard pattern in a case like this for exposing an additional non-generic version of the interface, so I can remove the generic type on the pager?

public class PagedListPager(IPagedList list)

Edit

I thought I'd also add the current way I've solved this problem and invite comments on whether it's a suitable solution:

public interface IPagedList // non-generic version
{
    IEnumerable<object> PageResults { get; }
    int CurrentPageIndex { get; }
    int TotalRecordCount { get; }
    int TotalPageCount { get; }        
    int PageSize { get; }
}


public class ConcretePagedList<T> : IPagedList<T>, IPagedList
{
    #region IPagedList<T> Members

    public IEnumerable<T> PageResults { get; set; }
    public int CurrentPageIndex { get; set; }
    public int TotalRecordCount { get; set; }
    public int PageSize { get; set; }

    #endregion

    #region IPagedList Members

    IEnumerable<object> IPagedList.PageResults
    {
        get { return PageResults.Cast<object>(); }
    }

    #endregion
}

Now I can pass ConcretePagedList<T> to non-generic classes/functions

fearofawhackplanet
  • 52,166
  • 53
  • 160
  • 253
  • Is `PagedListPager` a class or method declaration? – Greg B Jul 08 '11 at 10:30
  • @Gregg oops sorry I'll edit that. – fearofawhackplanet Jul 08 '11 at 10:34
  • I do not like having the properties defined in both interfaces as you could explicitly implement them to do different things. Eg, IPagedList.PageSize {get{return 8;}} IPageList.PageSize{get{return this.PageResults.Count();}} The only reason you have the second interface is to provide strong typing, so Marc's answer seems to remove the ability for the class to have different results on the properties which should not be different. – MPavlak Apr 22 '13 at 12:30

3 Answers3

31

My approach here would be to use new to re-declare the PageResults, and expose the T as a Type:

public interface IPagedList
{
    int CurrentPageIndex { get; }
    int TotalRecordCount { get; }
    int TotalPageCount { get; }        
    int PageSize { get; }

    Type ElementType { get; }
    IEnumerable PageResults { get; }
}   

public interface IPagedList<T> : IPagedList
{
    new IEnumerable<T> PageResults { get; }
}  

This will, however, require "explicit interface implementation", i.e.

class Foo : IPagedList<Bar>
{
    /* skipped : IPagedList<Bar> implementation */

    IEnumerable IPagedList.PageResults {
        get { return this.PageResults; } // re-use generic version
    }
    Type IPagedList.ElementType {
        get { return typeof(Bar); }
    }
}

This approach makes the API fully usable via both the generic and non-generic API.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks Marc, I like this it's quite similar to what I came up with but with a somewhat neater implementation. – fearofawhackplanet Jul 08 '11 at 11:18
  • @Moop that depends on how the signature is changing. Do you have a specific example? – Marc Gravell Aug 05 '14 at 07:15
  • @MarcGravell Something like this: `public interface IBag { IFruit GetNextObject();} public interface IBag where T : IFruit { new T GetNextObject();}` – Moop Aug 05 '14 at 16:03
7

One option is to create 2 interfaces such that:

    public interface IPagedListDetails
    {
        int CurrentPageIndex { get; }
        int TotalRecordCount { get; }
        int TotalPageCount { get; }
        int PageSize { get; }
    }

    public interface IPagedList<T> : IPagedListDetails
    {
        IEnumerable<T> PageResults { get; }
    }

And then your control:

public class PagedListPager(IPagedListDetails details)
Ankur
  • 33,367
  • 2
  • 46
  • 72
  • I had considered this but was of the impression that interface inheritance was generally not considered a good idea? – fearofawhackplanet Jul 08 '11 at 10:43
  • There is no prob in Interface inheritance, if you look around .NET BCL you will find that there are various generic interfaces which inherits from non generic classes – Ankur Jul 08 '11 at 11:00
  • 2
    @fearofawhackplanet: IMHO interface inheritance is vastly underused. IMHO, something like IList should have derived from IReadableByIndex, IMutableByIndex, IAppendable, and IRemovableByIndex; arrays should have implemented the first two but not the last two. – supercat Jul 08 '11 at 14:52
5

Define two interfaces, first

    public interface IPageSpecification
    {
        int CurrentPageIndex { get; }
        int TotalRecordCount { get; }
        int TotalPageCount { get; }        
        int PageSize { get; }
    }   

public interface IPagedList<T> : IPageSpecification
{
    IEnumerable<T> PageResults { get; }
}   

As you see, IPagedList is derived from IPageSpecification. In your method, only use IPageSpecification as parameter. In other cases, IPagedList - implementers of IPagedList would also contain data from IPageSpecification

archil
  • 39,013
  • 7
  • 65
  • 82