usando tipos objeto interfaces implementacion genericos generico genericas crear clases c# design-patterns generics interface open-generics

c# - objeto - tipos de genericos



Patrón para exponer la versión no genérica de la interfaz genérica (3)

Digamos que tengo la siguiente interfaz para exponer una lista paginada

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

Ahora quiero crear un control de paginación

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

El control de paginación no tiene interés en T (el contenido real de la lista). Solo requiere el número de páginas, la página actual, etc. Por lo tanto, la única razón por la que PagedListPager es genérico es para que se compile con el IPagedList<T> genérico IPagedList<T> .

¿Es este un olor de código? ¿Debería importarme que efectivamente tenga un genérico redundante?

¿Hay un patrón estándar en un caso como este para exponer una versión adicional no genérica de la interfaz, por lo que puedo eliminar el tipo genérico en el buscapersonas?

public class PagedListPager(IPagedList list)

Editar

Pensé que también agregaría la forma actual en la que resolví este problema e invitaba a comentar si es una solución adecuada:

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 }

Ahora puedo pasar ConcretePagedList<T> a clases / funciones no genéricas


Definir dos interfaces, primero

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

Como puede ver, IPagedList se deriva de IPageSpecification. En su método, solo use IPageSpecification como parámetro. En otros casos, IPagedList: los implementadores de IPagedList también contendrían datos de IPageSpecification


Mi enfoque aquí sería utilizar new para volver a declarar los PageResults , y exponer la T como un 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; } }

Sin embargo, esto requerirá una "implementación de interfaz explícita", es decir,

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

Este enfoque hace que la API sea completamente utilizable a través de la API tanto genérica como no genérica.


Una opción es crear 2 interfaces tales que:

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

Y luego tu control:

public class PagedListPager(IPagedListDetails details)