2010-08-23 14 views
19

¿Hay alguna forma de devolver una instancia de solo lectura de un objeto?C# readonly objeto

public class Person 
{ 
    public String FirstName { get; set; } 
    public String LastName { get; set; } 
} 

public class SomeClass 
{ 
    public SomeClass(Person manager) 
    { 
     if (manager == null) 
      throw new ArgumentNullException("manager"); 

     _manager = manager; 
    } 

    private readonly Person _manager; 
    public Person Manager 
    { 
     get { return _manager; } //How do I make it readonly period! 
    } 
} 

es la única manera de hacer esto mediante la devolución de un clon() para que los cambios se hacen para el clon y no el original? Sé que para Arrays hay una función para devolver el Array como de solo lectura. Ah, y sé que este es un tipo de referencia ... Me pregunto si hay alguna función oculta de C# para bloquear la parte de escritura.

Estaba tratando de crear una clase de contenedor genérico ReadOnly, pero no pude encontrar la manera de obtener las propiedades de solo lectura sin hacer una reflexión costosa y cosas por el estilo.

Ah, y realmente estoy tratando de evitar crear una segunda versión de la clase que sea solo de solo lectura. En ese punto, podría devolver el clon.

+1

Sobre la base de los comentarios: ¿Hay alguna conocida clase de contenedor genérico de "sólo lectura", que es rápido y ¿eficiente? El que traté de hacer tuvo que usar la reflexión, que no es realmente lo que quiero hacer si puedo evitarlo. –

Respuesta

33

Para evitar la creación de una clase adicional, podría hacer que la implemente como una interfaz IPerson que solo tiene propiedades de solo lectura.

public interface IPerson 
{ 
    string FirstName { get; } 
    string LastName { get; } 
} 
public class Person:IPerson 
{ 
    public String FirstName { get; set; } 
    public String LastName { get; set; } 
} 

public class SomeClass 
{ 
public SomeClass(Person manager) 
{ 
    if (manager == null) 
     throw new ArgumentNullException("manager"); 

    _manager = manager; 
} 

private readonly Person _manager; 
public IPerson Manager 
{ 
    get { return _manager; } //How do I make it readonly period! 
} 
} 
+1

+1, solución limpia al problema. –

+1

Existen muy buenas razones para usar interfaces en lugar de tipos concretos (y si 'Person' tiene algún comportamiento, lo recomendaría también) ... pero ¿cómo se ahorra algo de esfuerzo en lugar de crear una clase? –

+5

Me gusta esto también, pero ¿no sería fácil para ellos simplemente devolverlo a una clase de Persona? 'Person iCanChangeProperties = (Person) SomeClass.Manager;' Y el Cast no protege contra ellos usando esa referencia para hacer cambios a las propiedades ... Tal vez tenga que seguir clonando. Sin embargo, la interfaz ReadOnly sería agradable, por lo que el reflejo les dice que ** NO PUEDEN ** hacer las propiedades del setter. –

2

No existe tal función: ha cubierto sus opciones.

O clonar o hacer un tipo de solo lectura Person. El último enfoque es generalmente preferido porque la semántica es más clara: es obvio para quienes llaman que no deberían (y no pueden) modificar la instancia.

+0

Este enfoque es bastante común en la biblioteca estándar C# también. Ver cosas como 'String' /' StringBuilder' y 'Uri' /' UriBuilder'. Solo tiene sentido cuando el uso de solo lectura es mucho más numeroso, que es el caso para esos dos. – Ekevoo

0

No. Está buscando algo como C++ - estilo const -ness, y para variousreasons C# no tiene eso.

Sin embargo, los tipos anónimos son verdaderamente inmutables.

1

No hay una forma de que todas las propiedades de su objeto sean solo de forma externa desde la clase. En su ejemplo anterior, no puede hacer que las propiedades _manager sean solo de lectura, a menos que haya cambiado las propiedades dentro de la clase Persona para que sean de solo lectura.

Puede hacer que el conjunto de propiedades de la clase Persona sea interno, lo que significa que solo las clases dentro del mismo conjunto que Persona pueden cambiar las propiedades.

O, si hace que el colocador de las propiedades sea privado, solo el código dentro de Person puede cambiar los valores de las propiedades.

4

Se puede congelar objeto (lo hace inmutable) bajo ciertas condiciones con ayuda de Castle.DynamicProxy. Lee este blog post para más detalles.

5

puede transformar la clase de persona en un objeto inmutable, como a continuación ..

public class Person 
{ 
    public Person(string firstName, string lastName) 
    { 
     FirstName = firstName; 
     LastName = lastName; 
    } 

    public String FirstName { get; private set; } 
    public String LastName { get; private set; } 

} 
0

objetos anónimos son de sólo lectura.

1

Aquí hay otro ejemplo, basado en cómo se implementa List.AsReadOnly en .NET Framework 2.0.Un booleano (IsReadOnly) se utiliza en los métodos adecuados para evitar actualizaciones:

public class Person 
{ 
    private string _firstName; 
    public string FirstName 
    { 
     get { return _firstName; } 
     set 
     { 
      if (!IsReadOnly) _firstName = value; 
      else throw new AccessViolationException("Object is read-only."); 
     } 
    } 

    private string _lastName; 
    public string LastName 
    { 
     get { return _lastName; } 
     set 
     { 
      if (!IsReadOnly) _lastName = value; 
      else throw new AccessViolationException("Object is read-only."); 
     } 
    } 

    internal virtual bool IsReadOnly { get { return false; } } 

    public ReadOnlyPerson AsReadOnly() 
    { 
     return new ReadOnlyPerson(this); 
    } 

    public class ReadOnlyPerson : Person 
    { 
     private Person _person; 
     internal override bool IsReadOnly { get { return true; } } 

     internal ReadOnlyPerson(Person person) // Contructor 
     { 
      this._person = person; 
     } 
    } 
} 

para probarlo:

static void Main(string[] args) 
{ 
    Person p1 = new Person(); 
    p1.FirstName = "Joe"; 
    p1.LastName = "Bloe"; 
    Console.WriteLine("First = {0} Last = {1}", p.FirstName, p.LastName); 

    var p2 = p1.AsReadOnly(); 
    p2.FirstName = "Josephine"; // AccessViolationException 
} 
Cuestiones relacionadas