I contratti di codice .NET 4.0 hanno un ruolo da svolgere nei progetti di programmatore singolo?

4

Spesso mi ritrovo a chiedermi quali sono le migliori pratiche di programmazione applicate alla programmazione in solitario, poiché la maggior parte delle volte sono l'unico programmatore di un progetto. Ho appena iniziato a sperimentare con i Contratti di codice C # 4.0, e ho pensato di chiedere alla community quale utilità possono avere per un programmatore solista.

Ecco da dove vengo. Il più delle volte, quando incontro un'eccezione, significa che c'è un bug nel mio codice. Ci sono eccezioni definite (non è previsto il gioco di parole) a questo, come quando ho a che fare con qualcosa di esterno, come un file di testo, database o servizio, ma per la maggior parte, eccezioni significano I ha fatto qualcosa di sbagliato.

Segui questo semplice metodo:

public void MapEntity(PersonModel model, PersonEntity entity)
{
    entity.Name = model.Name;
    entity.Age = model.Age;
    entity.Updated = DateTime.Now;
}

Se model o entity sono nulli, questo metodo genererà un NullReferenceException .

Come unico consumatore del mio codice, so che non dovrei passare un riferimento null, ma se in qualche modo lo faccio, io voglio un'eccezione da lanciare, e io vuoi il debugger per saltare direttamente alla fonte dell'errore, così posso vedere cosa è successo.

Detto questo, potrei scrivere:

public void MapEntity(PersonModel model, PersonEntity entity)
{
    Contract.Requires(model != null);
    Contract.Requires(entity != null);
    entity.Name = model.Name;
    entity.Age = model.Age;
    entity.Updated = DateTime.Now;
}

Questo ha il vantaggio di indicare esplicitamente (a me stesso) che i due argomenti non possono essere nulli. Tuttavia, è molto improbabile che farei questo errore (perché so come funziona il metodo). Inoltre, se capisco correttamente cosa fa Requires() , il comportamento in fase di esecuzione se si incontra un argomento nullo sarebbe praticamente lo stesso. Quindi, non posso fare a meno di chiedermi, cosa ho ottenuto oltre a due linee di codice?

Come contrappunto, ecco un caso in cui vedo un valore definito nell'essere esplicito:

public User FindUser(string emailAddress)
{
    if (string.IsNullOrWhiteSpace(emailAddress)) return null;
    var users = DbContext.Users.Where(u => u.EmailAddress == emailAddress);
    Contract.Assert(users.Count() <= 1,
                    "The database appears to be corrupted:
                    More than one user was found for the provided email address.
                    Email addresses are supposed to be unique.");
    return users.SingleOrDefault();
}

Ma in questo caso, la ragione per cui vedo il valore nell'usare un contratto di codifica è indicare che qualcosa è andato storto che non è un bug di per sé (anche se potrebbe essere il risultato di un bug in un altro punto del sistema). / p>

Mi piacerebbe sapere se sono sulla strada giusta con il mio pensiero, o se c'è più valore nei contratti di codice di quanto mi rendo conto. Paga per utilizzare i contratti quando puoi esaminare e eseguire il debug del codice da solo o solo quando il codice verrà utilizzato da una terza parte?

    
posta devuxer 12.07.2011 - 01:40
fonte

2 risposte

11

Il vantaggio delle asserzioni (o dei contratti, che sono fondamentalmente una diversa presa di asserzioni) arriva quando il tuo codebase cresce abbastanza da non poter più contenere l'intera cosa nella tua memoria a breve termine. Soprattutto quando lo stai ancora sviluppando e cambiando le cose ogni giorno.

Quando hai scritto per la prima volta MapEntity , molto probabilmente lo hai scritto perché qualche altra routine richiedeva la sua funzionalità. Stavi lavorando all'altra routine in quel momento, e sai che non passerà alcun valore nullo. Ma tra qualche mese potresti scrivere qualcos'altro che ha bisogno della stessa funzionalità, quindi fallo chiamare anche MapEntity . Ma forse questo codice può avere un nulla in qualche modo e non hai pensato di controllarlo.

Senza affermazioni, a questo punto si ottiene un'eccezione di riferimento null. Con le affermazioni in atto, si ottiene un errore di asserzione (o qualsiasi altro contratto violato, in questo caso). In ogni caso, si ha un errore qui e si deve scavare nel codice per capire cosa sta succedendo. Osservi il codice e vedi che il problema è in MapEntity perché soffoca su uno dei parametri. Quindi c'è un bug in MapEntity , o la routine lo ha chiamato alimentandolo con un parametro non valido?

Uno dei vantaggi di un'asserzione / contratto è che documenta chiaramente il fatto che hai anticipato questo problema ed esplicitamente non volevo che accadesse. Questo rende capire come risolverlo così tanto più facile perché rende più facile determinare la fonte del problema. In questo caso, mostra chiaramente che il problema è nel chiamante e non in MapEntity .

D'altra parte, a volte ricevo un errore di asserzione nel codice che riceve parametri perfettamente cromatici, perché i requisiti sono cambiati. Questo mi aiuta a vedere cosa deve essere cambiato, ea volte non è così semplice come "l'argomento non deve essere nullo". A volte l'argomento deve avere determinate proprietà impostate su determinati modi, altrimenti i dati sarebbero stati danneggiati in modo silenzioso. Avere un'asserzione in atto rende più facile ricordare cosa stavo pensando quando l'ho scritto, quindi posso sistemare il codice più facilmente.

    
risposta data 12.07.2011 - 02:02
fonte
5

La risposta di Mason è eccellente, ma un'altra cosa da ricordare è che i contratti sono un po 'più di una semplice presa di asserzioni, se si utilizza lo strumento di analisi statica incluso nella versione Premium dei Contratti di codice Microsoft.

Se la tua funzione MapEntity() inizia richiedendo che entrambi i suoi argomenti non siano nulli, allora sì, otterrai un'asserzione se viene chiamata con un argomento nullo.

Supponiamo che tu abbia un codice come questo:

var model = new PersonModel();
var entity = GetPersonEntityFromDatabaseOrSomething(personId);
MapEntity(model, entity);

Lo strumento di analisi statica vedrà che model non è sicuramente nullo. Ma quel entity potrebbe essere, a seconda di cosa restituirà GetPersonEntityFromDatabaseOrSomething() . Ti avviserà di questo.

A questo punto, dovresti aggiungere una postcondizione a GetPersonEntityFromDatabaseOrSomething()

Contract.Ensures(entity != null);

Questo soddisferà lo strumento di analisi statica, dal momento che ora possiamo essere sicuri che il entity passato a MapEntity() non sia nullo. E se, a causa di un bug in GetPersonEntityFromDatabaseOrSomething() , provi a restituire un valore nullo (ad es. Id non trovato nel database e non lo gestisci correttamente), otterrai la tua affermazione anche prima - non al punto in cui si tenta di fare riferimento al null, ma più vicino al problema effettivo, in cui si tenta di restituire null da una funzione a cui si fa affidamento per restituire un oggetto non null.

    
risposta data 12.07.2011 - 02:33
fonte

Leggi altre domande sui tag