Pattern for exposing non-generic version of generic interface

fearofawhackplanet picture fearofawhackplanet · Jul 8, 2011 · Viewed 9k times · Source

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

Answer

Marc Gravell picture Marc Gravell · Jul 8, 2011

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.