2010-02-16 14 views
7

He estado tratando de envolver mi cabeza en torno a esta violación FXCop "DoNotDeclareReadOnlyMutableReferenceTypes"tipos de referencia de sólo lectura inmutables y Violación FXCop: DECLARAR leen tipos de referencia sólo mutables

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

Código de MSDN, que haría hacer que esta violación:

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

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

de la respuesta de Jon here y here, entiendo que el campo que contiene la referencia al objeto (en este caso SomeStringBuilder) es de solo lectura y no del objeto en sí (creado por new StringBuilder())

Tomando este ejemplo, ¿cómo cambiaría el objeto, una vez que el campo tiene una referencia al mismo? Me gusta Eric Lippert's example de cómo se puede cambiar la matriz de sólo lectura, y me gustaría ver algo similar para cualquier otro tipo de referencia mutable

Respuesta

3

Como la clase MutableReferenceTypes se presenta en la pregunta, no puede realmente mutarla desde ninguna llamada externa ya que el campo SomeStringBuilder es privado.

Sin embargo, la clase en sí podría mutar el campo. No lo hace actualmente, pero podría en una iteración posterior.

Aquí hay un ejemplo de método:

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

Al llamar al método Mutate va a mutar la clase porque SomeStringBuilder ahora habrá cambiado.

La inmutabilidad no solo se trata de la encarnación actual de su código, sino también de protegerse de futuros errores. No es necesario que todas las clases sean inmutables, pero es más seguro mantenerse constante si elige crear un tipo inmutable.

+0

agradable. Fantástico. Perfecto. – ram

+2

Solo un pequeño error: el campo está ** protegido **, no es privado, por lo tanto, es * absolutamente * mutable desde el exterior. Creo que * esto * es a lo que FXCop se opone. –

+0

@Konrad Rudolph: ¡Buena captura! Acabo de ver la primera palabra clave y me di cuenta de que no era un modificador de acceso, y por lo tanto debe haber predeterminado al valor predeterminado (sic). No me di cuenta de que las palabras clave habían cambiado. –

0

.Net tiene una lista de los tipos de referencia inmutables que están permitidos aquí, StringBuilder no es uno de ellos .

La queja es que lo que estás construyendo no es inmutable, aunque el constructor estático se llama una vez y la clase se inicializa una vez, eso es lo único que se mantiene, el resto es mutable. Un hilo puede llamar al .Append(), y luego a otro ... ves cómo el constructor de cadenas en sí mismo muta y realmente no es readonly, porque cambia constantemente estados/mutaciones.

Declarándolo readonly es realmente un nombre inapropiado ya que el objeto al que se hace referencia allí mismo está en constante cambio.

+1

¿De qué otra manera se debería distinguir entre un campo de tipo de referencia que siempre apuntará al mismo objeto, y que puede apuntar a diferentes objetos durante la vida útil del objeto que lo contiene? Alguien que no entiende qué tipos de referencia son puede confundirse, pero esa persona tiende a confundirse sobre muchas cosas a menos que o hasta que aprenda a comprender los tipos de referencia. – supercat

0

No puede cambiar la referencia, pero cualquier llamada en el objeto (mutable) cambia su estado.

Por lo tanto, como el SomeStringBuilder (en este ejemplo) es mutable, su contenido puede cambiar, lo que puede ser engañoso para los usuarios de la clase porque, por lo tanto, no es realmente "de solo lectura".

Básicamente, readonly no garantiza de ninguna manera que el objeto enemigo no cambie, simplemente dice que la referencia no cambia.

+1

El hecho de que una referencia no cambie puede ser vital para otras partes del código (por ejemplo, algún otro objeto puede hacer una referencia al StringBuilder y esperar poder obtener su valor); si bien uno debe ser consciente de que el objeto al que se hace referencia puede estar mutado, eso no implica que haya nada inútil en declarar la referencia en sí misma inmutable. Por cierto, incluso los delegados no siempre son inmutables; un delegado formado a partir de un mutador de estructura encajonará la estructura, y la estructura en caja será mutable. – supercat

0

No cambiaría el valor del objeto. Ese es el punto de la regla. Cualquier campo declarado como readonly debe ser de hecho, de solo lectura. Tener una referencia mutable de solo lectura es un oxímoron.Si puede cambiar el valor de lo que el campo "apunta" a, entonces en realidad ya no es de solo lectura. Realmente no hay diferencia funcional entre asignar el valor de todos los miembros de algún objeto A a algún objeto B representado por un campo o simplemente asignar A a ese campo (cuando son del mismo tipo), pero solo uno de Ellos es válido cuando el campo se declara de solo lectura, pero ya que puede cambiar efectivamente el valor de lo que se representa en el campo es como ya se indicó, no realmente solo

5

readonly significa que no puede cambiar la referencia de construcción posterior.

La postura oficial de FXCop es que recomienda que solo los tipos que no se pueden modificar se declaren readonly. Por lo tanto, algo como string está bien porque el valor del objeto no se puede cambiar. Sin embargo, el valor de StringBuilder puede cambiar pero hacerlo de solo lectura solo le impide asignar el campo a una instancia StringBuilder diferente o null después de que se ejecute el constructor.

No estoy de acuerdo con FXCop con respecto a esta regla. Mientras uno entienda que esto es simplemente una aplicación que la referencia no puede cambiar después de la construcción, entonces no hay confusión.

Tenga en cuenta que los tipos de valores se vuelven inmutables por la palabra clave readonly, pero los tipos de referencia no lo son.

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 para el código. Vinculación de tu código en MSDN. Habría marcado el tuyo como respuesta si respondiste antes. bueno de todos modos !! – ram

+0

Respuesta anterior, pero sigue siendo muy relevante. Code Analysis (como lo es ahora) todavía tiene algunas reglas tontas (como esta) que asumen que los codificadores son idiotas. Cualquier persona que entienda el idioma a medias sabe lo que significa 'readonly'. Lo primero que hago al configurar un nuevo proyecto es desactivar una tonelada de reglas de CA como CA2104. – 0b101010

Cuestiones relacionadas