2010-06-16 16 views
10

Sé cómo usar las propiedades y entiendo que invocan implícitamente los accesos subyacentes get y set, dependiendo de si estamos escribiendo o leyendo desde una propiedad.¿Esto no acaba con el propósito de tener propiedades de solo lectura?

static void Main(string[] args) 
{ 
    A a = new A(); 
    (a.b).i = 100; 

} 

class A 
{ 
    private B _b = new B(); 
    public B b 
    { 
     get { return _b; } 
    } 
} 
class B 
{ 
    public int i; 
} 

¿Qué código (a.b).i = 100; hace esencialmente es que get de acceso de primera propiedad devuelve una referencia a un objeto _b, y una vez que tengamos esta referencia, que son capaces de acceder _b’s miembros y cambiar sus valores.

Por lo tanto, en nuestro ejemplo, tener propiedad de solo lectura impide que el código externo cambie el valor de una variable de referencia _b, pero no impide que los códigos externos accedan a los miembros _b’s.

Parece que la propiedad solo puede detectar si estamos tratando de leer o escribir en una variable (en nuestra variable de caso _b) ubicada en la pila, mientras que no puede detectar si también estamos tratando de escribir a los miembros de un objeto al que apunta la variable en la pila (asumiendo que esta variable es de tipo de referencia).

a) ¿Pero eso no acaba con el propósito de tener propiedades de solo lectura? ¿No sería más efectivo si las propiedades tuvieran la capacidad de detectar también si estamos tratando de acceder a los miembros de un objeto devuelto por get accessor (suponiendo que el campo de respaldo es de un tipo de referencia)?

gracias

+0

@Oded: Este no es el tipo de 'readonly' sobre el que OP está preguntando. – Brian

+0

¿Devolver una copia? Si sabe que las clases son tipos de referencia, este comportamiento se vuelve obvio. No hay nada en el código anterior que diga que B debe ser inmutable, excepto que simplemente no le dio una propiedad de conjunto. – Puppy

Respuesta

13

imaginar una clase como esta:

public class A 
{ 
    private List<int> _myList<int> = new List<int>(); 
    public List<int> MyList { get { return _myList; } } 
} 

Ahora, los usuarios de la clase puede añadir y eliminar elementos de acceso y en la lista, pero no pueden sustituir a la propia lista. Esto es importante. Te permite hacer cosas dentro de la clase como asumir que el miembro _myList nunca es nulo, por ejemplo.

Dicho de una manera más general, este paradigma le permite definir una interfaz en su clase para que los usuarios puedan usar los tipos en las propiedades que expone, pero no pueden simplemente intercambiar instancias de tipos complejos debajo de usted.

+0

gracias a todos por su ayuda – flockofcode

27

La inmutabilidad no es transitiva; no se puede esperar que los objetos mutables en un acceso inmutable sean inmutables.

+1

Esta es una de las cosas que me gustaría ver en el futuro; inmutabilidad transitiva El lenguaje de programación D ya tiene esto, y es una buena manera de protegerse contra errores y simplifica mucho la concurrencia. – simendsjo

+0

@simendsjo: C++ también lo tiene :) Lamentablemente, 'const' -corrección es muy molesto y demasiado a menudo pasado por alto, razón por la cual no se incluyó en C# o Java –

+5

Me gusta esta respuesta, pero la pregunta implica una falta de comprensión de la mutabilidad, por lo que no estoy seguro de lo útil que es. – Greg

6

Por supuesto puede acceder B.i; es public. ¿Está pensando que como el _b es privado, todos los métodos deben ser privados cuando se obtienen a través del A? En ese caso, es bastante inútil ya que no podría usar B para nada.

19

Su referencia es de solo lectura, no es su objeto.

+1

Simple y conciso. +1 –

9
  1. No, no elimina el propósito de las propiedades de solo lectura.
  2. Es posible usar propiedades de solo lectura que no permiten al usuario cambiar los datos subyacentes. Por ejemplo, puede hacer que su propiedad devuelva un System.Collections.ObjectModel.ReadOnlyCollection aunque el tipo subyacente sea List. Esto, por supuesto, no evitará que el usuario cambie las propiedades de los artículos en la colección.
+0

Y en general, siempre tiene la opción de crear una interfaz/envoltorio de solo lectura para el tipo B, y hacer que solo sea accesible a través de la propiedad de solo lectura: este es el patrón que ReadOnlyCollection es (tristemente infrautilizado)) en vez de. –

2

Como punto menor, no es necesario escribir (a.b) .i = 100; sino simplemente

a.b.i = 100; 

Volver a su pregunta, no creo en contra del propósito. Todavía no puede hacer lo siguiente:

a.b = new B(); 

porque no hay un conjunto público(). Si desea que el miembro i de la clase B sea de solo lectura, puede hacer lo mismo que le hizo al miembro _b de la clase A al hacerlo privado y proporcionar un get público(), pero no establecido().Fuera de mi cabeza, hacer lo que propones puede llevar a muchas consistencias inesperadas (estoy seguro de que los diseñadores de idiomas no pasaron por alto esto).

+0

Pero aún puede hacer: /// función inocente void IDontDoAnything (A a) { a.B.DestroyEverything(); } – simendsjo

2

Totalmente dependiente de la situación, pero el acceso de solo lectura a un objeto mutable es un diseño de uso común. En muchos casos, simplemente quiere asegurarse de que el objeto en sí permanezca igual.

Algunas clases (como el objeto String en Java, y también creo en C#) son completamente inmutables, mientras que otras solo se pueden mutar parcialmente. Considere un estilo de objeto ActiveRecord para el cual la mayoría de los campos son mutables, pero el ID es inmutable. Si su clase tiene un ActiveRecord en una propiedad de solo lectura, las clases externas no pueden cambiarlo por otro objeto ActiveRecord y, por lo tanto, cambiar la ID, lo que podría romper las suposiciones dentro de su clase.

2

No estoy de acuerdo. Su propiedad es para la clase B, no para los miembros de la clase B. Esto significa que no puede asignar un nuevo objeto a b. No significa que los miembros públicos de B de repente se vuelvan privados.

5

Usted pregunta:

¿No se derrota a todo el propósito de tener propiedades de sólo lectura?

Pero mira: el miembro de su B.i es un campo público .

te pido que , entonces: ¿cuál es el propósito de tener un campo público? Solo tiene sentido si quiere que los usuarios de su código puedan cambiar el valor de ese campo. Si no desea que lo desee, debe ser un campo privado o (si desea proporcionar acceso de lectura pero no de escritura) una propiedad con un acceso privado set.

Así que ahí está su respuesta. private B _b cumple su función en el código que envió bastante bien (_b no puede ajustarse externamente a algo nuevo), al igual que public int i sirve su propósito igualmente bien (i puede ser cambiado externamente).

4

La inmutabilidad de referencia es una solicitud de función popular. Lástima que es tan dramáticamente no cumple con CLS. Muy pocos idiomas tienen esta noción, solo sé de C++ (pero no salgo mucho).

El problema clave que debe aplicar este CLR.C++ no necesita aplicar esto en tiempo de ejecución, solo se requiere un compilador de C++ para garantizar que se cumplan los contratos de const. No tiene soporte en absoluto para la interoperabilidad del lenguaje, más allá de un COM como Bolt-on.

Esto no va a volar en .NET, no tiene sentido declarar una referencia inmutable y que el compilador la verifique cuando otro idioma pueda pisotearla porque no tiene la sintaxis para expresar la inmutabilidad. Creo que lo conseguiremos algún día, no Real Soon.

+0

Tener inmunidad de referencia para tipos generales sería difícil. Tener tipos para ReadableArray (base para todos los tipos de matriz), ImmutableArray, SwappableArray y Array probablemente no habría sido tan difícil (si lo tuviera por druthers, String sería esencialmente una abreviatura de una matriz Immutable de Char, y métodos como SubString serían utilizables para cualquier tipo de InmutableArray). ReadableArray incluiría un método "AsImmutable", que podría clonar la matriz si es mutable, o simplemente devolverla si no es así. Sin embargo, probablemente no haya manera de exprimir esas características ahora. – supercat

2

readonly se aplica a la propiedad de clase, no al objeto al que hace referencia la propiedad. Le impide escribir a.b = new B();, y eso es todo lo que hace. No establece restricciones sobre lo que puede hacer con el objeto una vez que obtiene una referencia al mismo. Creo que lo que está descubriendo es que readonly tiene más sentido cuando se aplica a tipos de valores o tipos de clases inmutables.

2

Otro caso de uso:

interface INamedPerson 
{ 
    String Name { get; } 
} 

class Bob : INamedPerson 
{ 
    public String Name { get; set; } 
} 

class Office 
{ 
    // initialisation code.... 

    public INamedPerson TheBoss { get; } 

    public IEnumerable<INamedPerson> Minions { get; } 
} 

Ahora, si usted tiene una instancia de la Oficina, siempre y cuando no se va hacer trampa con los moldes, que haya acceso de sólo lectura a los nombres de todos, pero puede No cambies ninguno de ellos.

1

Ah. La encapsulación hace que la clase instanciada herede el nivel de acceso de la clase contenedora. Exponer el tipo B como una propiedad pública de tipo A. 'B.i' es público, por lo que debe ser accesible desde el exterior de la misma manera en que 'A.b' es público.

A.b devuelve una referencia de un tipo de acceso privado B, sin embargo, el tipo B tiene un campo de acceso público i. Según entiendo, puede establecer el campo i de B, pero no puede establecer la propiedad b de A externamente. La propiedad de tipo B de A es de solo lectura; sin embargo, la referencia al tipo B no define el mismo acceso de solo lectura a sus campos.

Estoy seguro de que puede modificar la definición de tipo B para satisfacer su necesidad del nivel de acceso de los campos o propiedades de B.

Cuestiones relacionadas