2011-07-08 15 views
26

Dire che ho la seguente interfaccia per esporre un elenco di pagingmodello per esporre versione non generica di un'interfaccia generica

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

Ora voglio creare un controllo di paging

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

Il controllo di paging ha nessun interesse in T (i contenuti effettivi della lista). Richiede solo il numero di pagine, la pagina corrente ecc. Quindi l'unico motivo per cui lo PagedListPager è generico è che verrà compilato con il parametro generico IPagedList<T>.

È un codice olfattivo? Dovrei preoccuparmi di avere effettivamente un ridondante generico?

C'è un modello standard in un caso come questo per esporre una versione non generica dell'interfaccia, quindi posso rimuovere il tipo generico sul cercapersone?

public class PagedListPager(IPagedList list) 

Modifica

ho pensato di aggiungere anche l'attuale modo ho risolto questo problema e invito i commenti che si tratti di una soluzione adatta:

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 
} 

Ora posso passare a ConcretePagedList<T> a classi/funzioni non generiche

+0

'PagedListPager ' una dichiarazione di classe o metodo? –

+0

@Gregg oops scusate lo modifico. – fearofawhackplanet

+0

Non mi piace avere le proprietà definite in entrambe le interfacce, poiché è possibile implementarle esplicitamente per fare cose diverse. Ad esempio, IPagedList.PageSize {get {return 8;}} IPageList .PageSize {get {return this.PageResults.Count();}} L'unica ragione per cui hai la seconda interfaccia è quella di fornire una digitazione forte, quindi la risposta di Marc sembra rimuovere la possibilità per la classe di avere risultati diversi sulle proprietà che non dovrebbero essere diverse. – MPavlak

risposta

27

Il mio approccio qui sarebbe utilizzare new per dichiarare nuovamente il PageResults ed esporre lo T come 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; } 
} 

Ciò, tuttavia, richiede "implementazione esplicita", cioè

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

Questo approccio permette l'API pienamente utilizzabile sia tramite l'API generico e non generico.

+0

Grazie Marc, mi piace che sia abbastanza simile a quello che ho trovato, ma con un'implementazione un po 'più ordinata. – fearofawhackplanet

+0

Eccellente: semplice, pulito e facile da leggere. – Chris

+1

Come funziona con Methods, al contrario di Properties? – Moop

4

definire due interfacce, primi

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

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

Come si vede, IPagedList è derivato da IPageSpecification. Nel tuo metodo, usa IPageSpecification come parametro. In altri casi, IPagedList - esecutori di IPagedList inoltre contengono i dati da IPageSpecification

7

Una possibilità è quella di creare 2 interfacce in modo tale che:

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

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

E poi il vostro controllo:

public class PagedListPager(IPagedListDetails details) 
+0

L'avevo considerato ma avevo l'impressione che l'ereditarietà dell'interfaccia non fosse generalmente considerata una buona idea? – fearofawhackplanet

+0

Non esiste alcun problema nell'ereditarietà dell'interfaccia, se si guardano attorno a .NET BCL si scoprirà che ci sono varie interfacce generiche che ereditano da classi non generiche – Ankur

+1

@fearofawhackplanet: l'ereditarietà dell'interfaccia IMHO è ampiamente sottoutilizzata. IMHO, qualcosa come IList dovrebbe derivare da IReadableByIndex, IMutableByIndex, IAppendable e IRemovableByIndex; gli array dovrebbero aver implementato i primi due ma non gli ultimi due. – supercat

Problemi correlati