È un'interfaccia con due proprietà di raccolta, in cui la seconda filtra la prima raccolta, idiomatica per C #?

5

Ho avuto una discussione interessante con un collega che ruotava attorno a come le persone interpretano l'uso di proprietà e metodi su un'interfaccia. Ad esempio, supponiamo di avere un blog con post in vari stati: Lavoro e pubblicazione.

Quando in "Working" l'autore sta ancora apportando modifiche e non dovrebbe essere visibile ai lettori. Quando "Pubblicato", beh ... è pubblicato e gli utenti finali possono leggerlo.

Diciamo anche che abbiamo un'interfaccia per il blog, che ho definito come:

public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> PublishedPosts { get; }
}

Il mio collaboratore era preoccupato che gli utenti di questa interfaccia potessero interpretare queste due proprietà come raccolte separate di oggetti. Ho capito dal momento che hai una proprietà "Post" e un'altra proprietà chiamata "Post pubblicati" che restituisce lo stesso tipo di oggetto che le persone farebbero l'assunzione corretta che Posts è una raccolta e PublishedPosts è una visualizzazione filtrata della raccolta Posts .

Il suo suggerimento era:

public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> GetPublishedPosts();
}

Sostanzialmente, sostituire la proprietà PublishedPosts con un metodo GetPublishPosts (). Ha detto che questo era più idiomatico per C #, perché un metodo comunica alle persone che usano l'interfaccia che stai eseguendo un'operazione sulla raccolta "Post" (filtrandola per stato). Non ho davvero visto alcuna documentazione formale per questo, ma ciò non impedisce alla comunità C # di inclinarsi in una direzione o nell'altra.

Ha una proprietà di raccolta e quindi metodi per filtrare la raccolta o due raccolte in cui la seconda filtra il primo idiomatico per C #? In tal caso, c'è documentazione formale ovunque?

    
posta Greg Burghardt 12.01.2017 - 22:15
fonte

3 risposte

6
IEnumerable<Post> PublishedPosts { get { return Posts.Where(p => p.Published) }; }

se vuoi un posto unico per la tua logica di filtraggio. Oppure, semplicemente filtrare secondo necessità:

var publishedPosts = myBlog.Posts.Where(p => p.Published);

La seconda versione non richiede di andare alla definizione della proprietà per determinare cosa fa. Non indovinare gli utenti della tua classe; se la denominazione (che è ciò che è veramente tutto) non chiarisce le cose, quindi usa nomi di proprietà migliori, fornisci commenti adeguati o lascia che vedano il codice sorgente.

Non consiglio l'uso di un metodo qui. I metodi implicano l'elaborazione e un ritardo di ritorno (cioè più di 50 ms), e non vi è alcuna elaborazione che avvenga qui oltre l'uso di la funzione di filtro. Un metodo implica anche che stai facendo delle copie dei post originali del blog piuttosto che restituire una raccolta di riferimenti, che probabilmente non è ciò che desideri.

Personalmente preferisco il filtro ad-hoc, perché ti consente di lavorare con una singola proprietà non ambigua contenente la raccolta di post del blog. Un luogo comune per la tua logica di filtraggio non è realmente necessario a meno che non pianifichi di revisionare la tua architettura in futuro e, se davvero ne hai bisogno, passa al filtro una funzione di ordine superiore:

Func<Post, bool> published = p => p.Published; // one place to change filter logic
var publishedPosts = myBlog.Posts.Where(published);

Guida di Microsoft:

Do use a method, rather than a property, in the following situations:

  • The operation is orders of magnitude slower than a field set would be.
  • The operation is a conversion, such as the Object.ToString method.
  • The operation returns a different result each time it is called, even if the parameters do not change. For example, the Guid.NewGuid method returns a different value each time it is called. 8 The operation has a significant and observable side effect.
  • The operation returns a copy of an internal state.
  • The operation returns an array.

Use a method where the operation returns an array because to preserve the internal array, you would have to return a deep copy of the array, not a reference to the array used by the property. This fact, combined with the fact that developers use properties as though they were fields, can lead to very inefficient code.

    
risposta data 12.01.2017 - 22:26
fonte
1

Se guardiamo a .NET CLR possiamo vedere alcuni esempi di insiemi e sottoinsiemi esposti dallo stesso oggetto. Ad esempio, considera le seguenti raccolte di uso comune da System.Web :

HttpRequest.Item - Restituisce tutte le coppie tag / valore nella richiesta

HttpRequest.Form.Item - Restituisce solo le coppie tag / valore rilevate nel corpo

HttpRequest.QueryString.Item - Restituisce solo le coppie tag / valore trovate nella querystring

La seconda e la terza collezione sono sottoinsiemi della prima raccolta di cui sopra, quindi hanno la stessa relazione set / sottoinsieme dei post del blog nella domanda posta dall'OP.

In base a questo esempio, potrei suggerire quanto segue:

interface IBlog : IEnumerable<Post>
{
    IEnumerable<Post> PublishedPosts { get;}
}
    
risposta data 13.01.2017 - 08:29
fonte
0

Per me IEnumerable<Post> GetPublishedPosts(); non viene più un filtro di IEnumerable<Post> PublishedPosts { get; }

public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> GetPublishedPosts();
} 

Per definizione un'interfaccia non definisce o limita un'implementazione
Non c'è nulla che fermi la seguente implementazione

public class Post
{ }
public interface IBlog
{
    IEnumerable<Post> Posts { get; }
    IEnumerable<Post> GetPublishedPosts();
}
public class Blog : IBlog
{
    private List<Post> posts = new List<Post>();
    private List<Post> postsPublished = new List<Post>();
    IEnumerable<Post> IBlog.Posts { get { return posts; } }
    IEnumerable<Post> IBlog.GetPublishedPosts() { return postsPublished; }
}
    
risposta data 16.01.2017 - 01:55
fonte

Leggi altre domande sui tag