2010-02-16 9 views
7

ho cercato di avvolgere la mia testa intorno a questa violazione FxCop "DoNotDeclareReadOnlyMutableReferenceTypes"immutabili tipi di riferimento di sola lettura e FxCop violazione: non dichiarano di leggere i tipi di riferimento solo mutabili

MSDN: http://msdn.microsoft.com/en-us/library/ms182302%28VS.80%29.aspx

codice da MSDN, che avrebbe causare questa violazione:

namespace SecurityLibrary 
{ 
    public class MutableReferenceTypes 
    { 
     static protected readonly StringBuilder SomeStringBuilder; 

     static MutableReferenceTypes() 
     { 
      SomeStringBuilder = new StringBuilder(); 
     } 
    } 
} 

Dalla risposta di Jon here e here, ho capito che il campo tenendo il riferimento all'oggetto (in questo caso SomeStringBuilder) è di sola lettura e non l'oggetto stesso (che viene creato da new StringBuilder())

Quindi, prendendo questo esempio, come dovrei cambiare l'oggetto stesso, una volta che il campo ha un riferimento ad esso? Mi piace il Eric Lippert's example di come l'array di sola lettura può essere modificato e vorrei vedere qualcosa di simile per qualsiasi altro tipo di riferimento mutabile

risposta

3

Come la classe MutableReferenceTypes è presentato nella questione, non si può davvero mutarlo da qualsiasi chiamante esterno in quanto il campo SomeStringBuilder è privato.

Tuttavia, la classe stessa potrebbe modificare il campo. Al momento non lo fa, ma potrebbe in un iterazione successiva.

Ecco un metodo di esempio:

public static void Mutate() 
{ 
    SomeStringBuilder.AppendLine("Foo"); 
} 

chiamata al metodo Mutate sarà mutare la classe perché SomeStringBuilder ora sono cambiate.

L'immutabilità non riguarda solo l'attuale incarnazione del codice, ma anche la protezione da futuri errori. Non tutte le classi devono essere immutabili, ma è più sicuro rimanere coerenti se si decide di creare un tipo immutabile.

+0

bello. Fantastico. Perfezionare. – ram

+2

Solo un piccolo errore: il campo è ** protetto **, non privato, quindi è * assolutamente * mutabile dall'esterno. Immagino che * questo * sia ciò a cui FXCop si oppone. –

+0

@Konrad Rudolph: buona cattura! Ho appena visto la prima parola chiave e ho notato che non si trattava di un modificatore di accesso, e quindi deve essere impostato come predefinito (sic). Non ho notato che le parole chiave erano state cambiate. –

0

.Net ha un elenco di tipi di riferimento immutabili che sono consentiti qui, StringBuilder non è uno di loro .

La lamentela è che ciò che state costruendo non è immutabile, anche se il costruttore statico viene chiamato una volta e la classe è inizializzata una volta, questo è tutto ciò che rimane uguale, il resto è mutabile. Un thread può chiamare .Append(), quindi un altro ... si vede come lo stesso generatore di stringhe muta e non sia realmente readonly, perché cambia continuamente stati/mute.

Dichiararlo readonly è davvero un termine improprio poiché l'oggetto a cui ci si riferisce è in continua evoluzione.

+1

In quale altro modo si dovrebbe distinguere tra un campo di tipo di riferimento che punta sempre allo stesso oggetto e uno che può puntare a oggetti diversi durante la vita dell'oggetto contenitore? Qualcuno che non capisce quali tipi di riferimento possono essere confusi, ma tale persona è suscettibile di essere confusa su molte cose a meno che o fino a quando non impari a capire i tipi di riferimento. – supercat

0

Non è possibile modificare il riferimento, ma qualsiasi chiamata sull'oggetto (modificabile) cambia stato.

Pertanto, poiché lo SomeStringBuilder (in questo esempio) è mutabile, il suo contenuto potrebbe cambiare, il che potrebbe essere fuorviante per gli utenti della classe perché non è quindi realmente "di sola lettura".

Fondamentalmente, readonly non garantisce in alcun modo che l'oggetto non cambi, ma semplicemente dice che il riferimento non cambia.

+1

Il fatto che un riferimento non cambi può essere vitale per altre parti del codice (ad esempio qualche altro oggetto può prendere un riferimento allo StringBuilder e aspettarsi di essere in grado di afferrarne il valore); mentre si dovrebbe certamente essere consapevoli che l'oggetto referenziato può essere mutato, ciò non implica che ci sia qualcosa di inutile nel dichiarare il riferimento stesso immutabile. A proposito, anche i delegati non sono sempre immutabili; un delegato formato da un mutatore di struttura inquadrerà la struttura e la struttura in scatola sarà mutabile. – supercat

0

Non si modifica il valore dell'oggetto. Questo è il punto della regola. Qualsiasi campo dichiarato come readonly dovrebbe essere quello, di sola lettura. Avere un riferimento facilmente mutabile è un ossimoro.Se riesci a cambiare il valore di ciò che il campo "punta", allora non è più in sola lettura. Non c'è davvero alcuna differenza funzionale tra l'assegnazione del valore di tutti i membri di qualche oggetto A a qualche oggetto B rappresentato da un campo o semplicemente l'assegnazione di A a quel campo (quando sono dello stesso tipo) tuttavia solo uno di loro è valido quando il campo è dichiarato readonly ma dal momento che è possibile ignorare il valore di ciò che viene rappresentato dal campo è come già dichiarato non in sola lettura

5

readonly significa che non è possibile modificare la post-costruzione di riferimento.

La posizione ufficiale di FXCop è che raccomanda che solo i tipi che non possono essere modificati debbano essere dichiarati readonly. Quindi qualcosa come un string va bene perché il valore dell'oggetto non può essere cambiato. Tuttavia, il valore di StringBuilder può essere modificato ma, in tal modo, solo in lettura, impedisce l'assegnazione del campo a un'altra istanza StringBuilder o null dopo l'esecuzione del costruttore.

Non sono d'accordo con FXCop su questa regola. Finché si comprende che si tratta semplicemente di un'applicazione che il riferimento non può cambiare dopo la costruzione, allora non c'è confusione.

Si noti che i tipi di valore sono resi immutabili dalla parola chiave readonly ma i tipi di riferimento non lo sono.

namespace SecurityLibrary 
{ 
    public class MutableReferenceTypes 
    { 
     static protected readonly StringBuilder SomeStringBuilder; 

     static MutableReferenceTypes() 
     { 
      // allowed 
      SomeStringBuilder = new StringBuilder(); 
     } 

     void Foo() 
     { 
      // not allowed 
      SomeStringBuilder = new StringBuilder(); 
     } 

     void Bar() 
     { 
      // allowed but FXCop doesn't like this 
      SomeStringBuilder.AppendLine("Bar"); 
     } 
    } 
} 
+0

+1 per il codice. Collegamento del codice in MSDN. Avresti contrassegnato il tuo come risposta se avessi risposto prima. bravo comunque !! – ram

+0

Vecchia risposta, ma ancora molto rilevante. L'analisi del codice (com'è ora) ha ancora alcune regole stupide (come questa) che presuppongono che i programmatori siano idioti. Chiunque comprenda a metà strada anche la lingua sa cosa significa "readonly". La prima cosa che faccio quando si imposta un nuovo progetto è disabilitare una tonnellata di regole CA come CA2104. – 0b101010

Problemi correlati