Pregunta

Tengo un Bar clase con un campo privado que contiene el tipo de referencia Foo. Me gustaría exponer Foo en una propiedad pública, pero no quiero que los consumidores de la propiedad a ser capaz de alterar Foo ... Sin embargo, debería ser alterable internamente por Bar, es decir, no puedo hacer que el readonly campo.

Así que lo que me gustaría es:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

... que por supuesto no es válido. Yo sólo podría volver un clon de Foo (assumming que es IClonable), pero esto no es evidente para el consumidor. ¿Debo cambiar el nombre de la propiedad a FooCopy ?? ¿Debería ser un método GetCopyOfFoo en su lugar? ¿Qué se tiene en cuenta las mejores prácticas? Gracias!

¿Fue útil?

Solución

Parece que lo que busca es el equivalente a "const" de C ++. Esto no existe en C #. No hay manera de indicar que los consumidores no pueden modificar las propiedades de un objeto, sino otra cosa puede (suponiendo que los miembros mutantes son públicos, por supuesto).

Se puede devolver un clon del Foo como se sugiere, o posiblemente un Ver en el Foo, como ReadOnlyCollection hace por colecciones. Por supuesto, si usted podría hacer Foo un tipo inmutable, que haría la vida más fácil ...

Tenga en cuenta que hay una gran diferencia entre hacer el campo de sólo lectura y hacer que el propio objeto inmutable.

En la actualidad, el tipo en sí podría cambiar las cosas en ambos sentidos. Se podría hacer:

_Foo = new Foo(...);

o

_Foo.SomeProperty = newValue;

Si sólo necesita ser capaz de hacer el segundo, el campo podría ser de sólo lectura, pero todavía tiene el problema de la gente ir a buscar la propiedad de ser capaz de mutar el objeto. Si sólo se tiene que hacer el primero, y en realidad es Foo o bien ya inmutable o podría hacerse inmutable, sólo puede proporcionar una propiedad que sólo tiene el "captador" y se le multa.

Es muy importante que entiende la diferencia entre cambiar el valor del campo (para hacerlo se refiere a una instancia diferente) y cambiar el contenido del objeto que el campo se refiere a.

Otros consejos

Desafortunadamente, no hay manera fácil de evitar esto en C # en este momento. Se podría extraer el "leer sólo una parte" de Foo en una interfaz y dejar que su declaración de bienes que en lugar de Foo.

Para hacer una copia, un ReadOnlyCollection, o tener Foo ser inmutables son por lo general los tres mejores rutas, como ya he especulado.

A veces a favor de métodos en lugar de propiedades cuando estoy haciendo nada más importante que la simple devolución del terreno subyacente, dependiendo de la cantidad de trabajo que está involucrado. Los métodos implican que algo está pasando y recaudar más de una bandera para los consumidores cuando están usando su API.

Objetos "Clonación" Foo el que recibe y da la espalda a cabo es una práctica normal llamado copia defensiva . A menos que haya algún efecto secundario no se ve a la clonación que será visible para el usuario, no hay absolutamente ninguna razón para no hacerlo. A menudo es la única manera de proteger los datos privados internos de sus clases, especialmente en C # o Java, donde la idea de C ++ const no está disponible. (Es decir, hay que hacerlo con el fin de crear objetos verdaderamente adecuadamente inmutables en estos dos idiomas.)

Solo para aclarar, los posibles efectos secundarios serían cosas como su usuario (razonablemente) esperando que se devolverá el objeto inicial, o algunos de los recursos en poder de Foo que no va a ser clonado correctamente. (En cuyo caso, ¿qué está haciendo la implementación IClonable?!)

Si no desea que nadie meterse con su estado ... no lo exponga! Como otros han dicho, si hay algo que necesita para ver su estado interno, proporcionar una representación inmutable de la misma. Por otra parte, lograr que los clientes dicen para hacer algo (en Google de "decir no preguntar"), en lugar de hacerlo ellos mismos.

Para aclarar el comentario de Jon Skeet se puede hacer un punto de vista, que es una clase contenedora inmutable de lo mutable Foo. He aquí un ejemplo:

class Foo{
 public string A{get; set;}
 public string B{get; set;}
 //...
}

class ReadOnlyFoo{
  Foo foo; 
  public string A { get { return foo.A; }}
  public string B { get { return foo.B; }}
}

En realidad se puede reproducir el comportamiento de C ++ const en C # - sólo hay que hacerlo de forma manual

.

Lo que es Foo, la única forma de la persona que llama puede modificar su estado es llamando a métodos en él o configuración de las propiedades.

Por ejemplo, Foo es de tipo FooClass:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get { ... }
        set { ... }
    }
}

Así pues, en su caso, eres feliz para ellos obtener la propiedad Message, pero no configurarlo, y definitivamente no está contento con ellos llamando a ese método.

Así define una interfaz que describe lo que están autorizados a hacer:

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}

Hemos dejado a cabo el método mutante y sólo queda en el captador en la propiedad.

A continuación, añadir que la interfaz a la lista de base de FooClass.

Ahora en su clase con la propiedad Foo, que tiene un campo:

private FooClass _foo;

Y una propiedad de captador:

public IFooConst Foo
{
    get { return _foo; }
}

Esto básicamente reproduce por parte precisamente lo que la palabra clave const ++ C haría automáticamente. En términos pseudo-C ++, una referencia de tipo const Foo & es como un tipo generado automáticamente que sólo incluye aquellos miembros de Foo que fueron marcados como miembros const. Traduciendo esto en alguna versión teórica futuro de C #, que le declara FooClass como esto:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get const { ... }
        set { ... }
    }
}

Realmente todo lo que he hecho se fusiona la información de IFooConst de nuevo en FooClass, mediante el etiquetado de un miembro de seguro con una nueva palabra clave const. Así pues, en cierto modo, la adición de una palabra clave const no añadiría mucho a la lengua, además de un enfoque formal de este patrón.

A continuación, si tuviera una referencia a un objeto const FooClass:

const FooClass f = GetMeAFooClass();

Sólo sería capaz de llamar a los miembros const en f.

Tenga en cuenta que si la definición FooClass es público, la persona que llama podría echó una IFooConst en un FooClass. Pero pueden hacer que en C ++ también -. Se llama "arrojando const" e implica un operador especial llamado const_cast<T>(const T &)

También está el tema de las interfaces no ser muy fácil de evolucionar entre las versiones de su producto. Si un tercero puede implementar una interfaz se define (que son libres de hacer si se lo puede ver), entonces no se puede añadir nuevos métodos a que en futuras versiones sin necesidad de otros para recompilar el código. Pero eso es sólo un problema si usted está escribiendo una biblioteca extensible para que se basen otros. Tal vez una característica const incorporada resolvería este problema.

Estaba pensando en las cosas de seguridad similares. Probablemente hay un camino. Muy claro, pero no corta. La idea general es bastante simple. Sin embargo siempre he encontrado algunas maneras alrededor de lo que nunca lo probaron. Sin embargo, se puede comprobar que -. Tal vez funcione para usted

Este es el pseudo código, pero espero idea detrás de esto es clara

public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);

class Foo {
  private Foo();
  public Foo(RullerCoronation followMyOrders) {
     followMyOrders(SetMe);
  }

  private SetMe(string whatToSet, string whitWhatValue) {
     //lot of unclear but private code
  }
}

Así que en la clase que crea esta propiedad tiene acceso al método SetMe, pero sigue siendo privada de modo excepto por el creador de Foo ve unmutable.

Sin embargo para cualquier cosa más grande que algunas propiedades Esto probablemente se convirtió pronto súper desastre - es por eso que siempre se prefieren otras maneras de encapsulación. Sin embargo, si es super importante que no permite que el cliente para cambiar Foo, que esto es una alternativa.

Sin embargo, como ya he dicho, esto es sólo teoría.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top