Proprietà delle collezioni e liste di inizializzazione nella progettazione dell'API .Net

1

Quanto segue è una citazione dalle linee guida di progettazione della struttura di Microsoft:

Collection Properties and Return Values

X DO NOT provide settable collection properties.

Users can replace the contents of the collection by clearing the collection first and then adding the new contents. If replacing the whole collection is a common scenario, consider providing the AddRange method on the collection.

Sono d'accordo con la linea guida - tuttavia ritengo che combini male con la sintassi di inizializzazione C #.

public class RequestParameter
{
    Collection<string> FilesToDownload { get; } = new Collection<string>();
    Collection<string> FilesToRemove { get; } = new Collection<string>();
}

Ciò impedisce all'utilizzatore dell'API di fare qualcosa come:

client.SendRequest(new RequestParameter
{
    FilesToDownload = { itemsToDownload.Select( item => item.path ) },
    FilesToRemove = { GetFilesToRemove() },
});

Ci sono quattro alternative che ho trovato:

Costruttori con parametri denominati

Il costruttore dovrebbe definire tutti i campi tipo come parametri, in quanto non voglio renderlo un caso comune che gli utenti debbano mixare parametri del costruttore e sintassi dell'inizializzatore.

Ciò rende i costruttori non compatibili all'indietro se abbiamo bisogno di aggiungere nuovi campi al tipo. Questa è un'API su un'API basata su JSON in cui voglio riservare la possibilità di aggiungere campi aggiuntivi ai tipi senza infrangere i nostri clienti.

Proprietà impostabili

Ignorare la linea guida "no setter" sarebbe una via d'uscita "facile".

Aggiungi un metodo Add(IEnumerable<T>) sulle raccolte

Pragmaticamente questo permetterebbe la sintassi che voglio per gli inizializzatori. Tuttavia, rompe le solite aspettative di Add rispetto a AddRange sui tipi di raccolta.

Aggiungi una proprietà IEnumerable<T> Values impostabile sulle raccolte

La sintassi dell'inizializzatore passerebbe a FilesToRemove = { Values = myItems } . Ancora una volta il purista in me non gradisce questa proprietà extra solo per permettere di usare la sintassi dell'inizializzatore per definire l'intera collezione.

Ci sono altre alternative? Un modo ideale sarebbe qualcosa di simile a operator= di overloading nella sintassi di inizializzatore per consentire a Foo = bar di richiamare una funzione personalizzata che potrebbe fare la copia invece di inizializzare l'intera raccolta ma, per quanto ne so, C # /. Net framework non lo fa t supportare qualcosa di simile.

Questo stesso problema si applica anche agli inizializzatori di oggetti in un certo modo. L'API con cui sto lavorando richiede che determinati campi JSON abbiano un valore e non siano nulli. Nell'API .Net ho voluto modellare queste proprietà come proprietà di sola lettura con il valore predefinito:

public ItemClass Item { get; } = new ItemClass();

Questo avrebbe reso chiaro all'utente quali proprietà possono essere nulle e quali non possono ma ha impedito all'utente di impostarle usando i costruttori di convenienza:

new Item
{
    Item1 = ItemClass.FileItem( "storage", "path" ),
    Item2 = ItemClass.UrlItem( "url" ),
}
    
posta Mikko Rantanen 13.11.2016 - 16:40
fonte

1 risposta

2

Potresti pensarci troppo.

Se

new RequestParameter
{
    FilesToDownload = ...,
    FilesToRemove = ...
}

è un modello di costruzione che ti aspetti di essere comune, perché non fornire un sovraccarico pubblico del costruttore, ad es.

public class RequestParameter
{
    public RequestParameter(Collection<string> filesToDownload, Collection<string> filesToRemove)
    {
        FilesToDownload = filesToDownload;
        FilesToRemove = filesToRemove;
    }

    public RequestParameter()
        : this(new Collection<string>(), new Collection<string>())
    {
    }

    public Collection<string> FilesToDownload { get; protected set; }
    public Collection<string> FilesToRemove { get; protected set; }
}

    
risposta data 14.11.2016 - 09:05
fonte

Leggi altre domande sui tag