2011-07-08 19 views
26

Decir que tengo la siguiente interfaz para exponer una lista paginadapatrón para exponer versión no genérica de interfaz genérica

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

Ahora queremos crear un control de aviso

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 aviso tiene sin 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 cual PagedListPager es genérico es para que compile con el parámetro genérico IPagedList<T>.

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

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

public class PagedListPager(IPagedList list) 

Editar

pensé que también añadiría la forma actual He resuelto este problema y solicitar comentarios sobre si se trata de 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

+0

¿Es 'PagedListPager ' una declaración de clase o método? –

+0

@Gregg uy disculpe voy a editar eso. – fearofawhackplanet

+0

No me gusta tener las propiedades definidas en ambas interfaces ya que podría implementarlas explícitamente para hacer cosas diferentes. Por ejemplo, IPagedList.PageSize {get {return 8;}} IPageList .PageSize {get {return this.PageResults.Count();}} La única razón por la que tiene la segunda interfaz es proporcionar tipado fuerte, por lo que la respuesta de Marc parece eliminar la posibilidad de que la clase a tener diferentes resultados en las propiedades que no debería ser diferente. – MPavlak

Respuesta

27

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

Esto, sin embargo, requieren "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 plenamente utilizable tanto a través de la API genéricos y no genéricos.

+0

Gracias Marc, me gusta esto, es bastante similar a lo que se me ocurrió, pero con una aplicación un tanto más ordenado. – fearofawhackplanet

+0

Excelente: simple, limpio y fácil de leer. – Chris

+1

¿Cómo funciona esto con los métodos, en comparación con las propiedades? – Moop

4

definen dos interfaces, primeros

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 se ve, IPagedList se deriva de IPageSpecification. En su método, solo use IPageSpecification como parámetro. En otros casos, IPagedList - ejecutores de IPagedList sería también contienen datos de IPageSpecification

7

Una opción es crear 2 interfaces de modo 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 de su control:

public class PagedListPager(IPagedListDetails details) 
+0

Lo había considerado, pero tenía la impresión de que la herencia de la interfaz generalmente no se consideraba una buena idea. – fearofawhackplanet

+0

No hay prob en la herencia de interfaces, si nos fijamos en torno .NET BCL se dará cuenta de que hay varias interfaces genéricas que hereda de clases no genéricos – Ankur

+1

@fearofawhackplanet: En mi humilde opinión interfaz de la herencia está infrautilizado enormemente. En mi humilde opinión, algo así como IList debe haber derivado de IReadableByIndex, IMutableByIndex, IAppendable y IRemovableByIndex; las matrices deberían haber implementado las dos primeras, pero no las dos últimas. – supercat

Cuestiones relacionadas