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)