Шаблон для показа неуниверсальной версии универсального интерфейса - PullRequest
26 голосов
/ 08 июля 2011

Допустим, у меня есть следующий интерфейс для отображения списка страниц

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

Теперь я хочу создать пейджинговый элемент управления

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

Пейджинговый элемент управления не заинтересован в T (фактическое содержимое списка). Требуется только количество страниц, текущая страница и т. Д. Таким образом, единственная причина, по которой PagedListPager является универсальной, заключается в том, что она будет компилироваться с универсальным параметром IPagedList<T>.

Это запах кода? Должен ли я позаботиться о том, чтобы у меня фактически был избыточный универсальный шаблон?

Существует ли в таком случае стандартный шаблон для предоставления дополнительной неуниверсальной версии интерфейса, чтобы я мог удалить универсальный тип на пейджере?

public class PagedListPager(IPagedList list)

Редактировать

Я подумал, что я также добавлю текущий способ, которым я решил эту проблему, и приглашаю комментировать, является ли это подходящим решением:

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
}

Теперь я могу передать ConcretePagedList<T> неуниверсальным классам / функциям

Ответы [ 3 ]

29 голосов
/ 08 июля 2011

Мой подход заключается в использовании new для повторного объявления PageResults и выставления T как 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; }
}  

Это, однако, потребует «явной реализации интерфейса», то есть

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

Этот подход делает API полностью пригодным для использования как через универсальный, так и не универсальный API.

7 голосов
/ 08 июля 2011

Один из вариантов - создать 2 интерфейса, таких как:

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

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

И затем ваш контроль:

public class PagedListPager(IPagedListDetails details)
4 голосов
/ 08 июля 2011

Определить два интерфейса, первый

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

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

Как видите, IPagedList является производным от IPageSpecification. В вашем методе используйте только IPageSpecification в качестве параметра. В других случаях IPagedList - разработчики IPagedList также будут содержать данные из IPageSpecification

...