2011-02-23 28 views
28

En una pregunta sobre Best practices for C# pattern validation, el más alto votaron respuesta dice:¿Cómo crear objetos inmutables en C#?

que tienden a realizar todas mis validación en el constructor. Esto es obligatorio porque casi siempre creo objetos inmutables.

¿Cómo se crea exactamente un objeto inmutable en C#? ¿Utiliza la palabra clave readonly?

¿Cómo funcionaría esto exactamente si desea validar en el constructor de su clase de modelo generado por Entity Framework?

¿Se vería abajo?

public partial readonly Person 
{ 
    public Person() 
} 
+6

posible duplicado de [¿Cómo creo una clase inmutable?] (Http://stackoverflow.com/questions/352471/how-do-i-create-an-immutable-class) –

+4

No es un duplicado: porque esto la pregunta es sobre la validación, no la creación. – ChrisW

+3

Pregunto sobre validación, no creación. –

Respuesta

66

La pregunta interesante aquí es su pregunta de los comentarios:

¿De qué objeto tendría usted que no es necesario modificar los valores en algún momento? Supongo que no es una clase modelo, ¿correcto? Tuve que cambiar el nombre de una persona en mi base de datos; esto no encaja con esta idea.

Bueno, considere las cosas que ya son inmutables. Los números son inmutables. Una vez que tienes el número 12, es 12. No puedes cambiarlo. Si tiene una variable que contiene 12, puede cambiar el contenido de la variable a 13, pero está cambiando la variable, no el número 12.

Lo mismo con cuerdas. "abc" es "abc" y nunca cambia. Si tiene una variable que contiene "abc", puede cambiarla a "abcd", pero eso no cambia "abc", que cambia la variable.

¿Qué tal una lista? {12, "abc"} es la lista que es 12 seguida de "abc", y esa lista nunca cambia. La lista {12, "abcd"} es una lista diferente .

Y ahí es donde las cosas se descarrilan. Porque en C# puedes hacerlo de cualquier manera. Puede decir que hay identidad referencial entre esas dos listas si se permite que las listas muten sus contenidos sin cambiar su identidad.

Golpeas el clavo directamente en la cabeza cuando hablas del "modelo". ¿Estás modelando algo que cambia? Si es así, entonces es posible que sea prudente modelarlo con un tipo que cambie. El beneficio de eso es que las características del modelo coinciden con el sistema que se modela. El inconveniente es que se vuelve muy complicado hacer algo así como una funcionalidad de "deshacer", donde "deshacer" un cambio.

Es decir, si muta {12, "abc"} a {12, "abcd"} y luego desea deshacer la mutación, ¿cómo lo hace? Si la lista es inmutable, simplemente manténgase cerca de ambos valores y elija cuál quiere que sea el valor "actual". Si la lista es mutable, entonces debe tener la lógica de deshacer para mantener una "función de deshacer" que sepa cómo deshacer la mutación.

En cuanto a su ejemplo específico, puede crear una base de datos inmutable. ¿Cómo cambias el nombre de alguien en tu base de datos inmutable? Tu no Crea una nueva base de datos que tiene los datos que desea. El truco con los tipos inmutables es hacerlo de manera eficiente, sin copiar miles de millones de bytes. El diseño de estructura de datos inmutables requiere encontrar formas inteligentes de compartir estados entre dos estructuras casi idénticas.

2

Un objeto de valor inmutable es un objeto de valor que no se puede cambiar. No se puede modificar su estado, usted tiene que crear otros nuevos

consulte el blog de Eric Lippert:

tipos de inmutabilidad http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx

Tenga una mirada en

Immutable object pattern in C# - what do you think?

+0

Ok, ¿entonces la premisa básica de la respuesta sobre la que estoy basando esta pregunta, es que una vez que pasa el constructor puede ser 100% certificado como kosher y válido? ¿Qué tipo de objeto tendría usted que no necesita modificar los valores en algún momento? Supongo que no es una clase modelo, ¿correcto? Tuve que cambiar el nombre de una persona en mi base de datos; esto no encaja con esta idea. –

9

Declarar todos campos de solo lectura es un buen paso hacia la creación de un objeto inmutable, pero esto solo no es suficiente. Esto se debe a que un campo de solo lectura todavía puede ser una referencia a un objeto mutable.

En C# no se aplica la inmutabilidad por parte del compilador. Sólo tienes que tener cuidado.

+2

..A veces puede devolver una versión inmutable (o de solo lectura) de esos campos mutables, p. ['List .AsReadOnly'] (http://msdn.microsoft.com/en-us/library/e78dcd75.aspx) para sus listas. – MasterMastic

+0

+1 para la última línea. De hecho, C# no proporciona azúcar para crear un nuevo objeto a partir de un objeto existente con una propiedad modificada. Incluso si fuera posible hacer un objeto inmutable, crear una nueva copia es demasiado tedioso. – nawfal

1

¿Cómo funcionaría esto exactamente si desea validar en el constructor de su clase de modelo generado por Entity Framework?

No funcionaría en este contexto porque EF requiere que las propiedades de la clase de entidad sean públicas, de lo contrario no puede instanciarlas.

Pero puede usar objetos inmutables en su código.

6

Esta pregunta tiene dos aspectos:

  1. tipo inmutable cuando se crea una instancia de objeto
  2. tipo inmutable cuando EF objeto instantiate

El primer aspecto exige sturcture así:

public class MyClass 
{ 
    private readonly string _myString; 
    public string MyString 
    { 
    get 
    { 
     return _myString; 
    } 
    } 

    public MyClass(string myString) 
    { 
    // do some validation here 

    _myString = myString; 
    } 
} 

Ahora el problema - EF. EF requiere un constructor sin parámetros y EF debe tener definidores en las propiedades. Pregunté muy similar question here.

Su tipo debe verse como:

public class MyClass 
{ 
    private string _myString; 
    public string MyString 
    { 
    get 
    { 
     return _myString; 
    } 
    private set 
    { 
     _myString = value; 
    } 
    } 

    public MyClass(string myString) 
    { 
    // do some validation here 

    _myString = myString; 
    } 

    // Not sure if you can change accessibility of constructor - I can try it later 
    public MyClass() 
    {} 
} 

También debe informar a EF sobre el colocador de la propiedad privada MiCadena - esto se configura en las propiedades de archivo en enitity EDMX. Obviamente no habrá validación cuando EF materialice objetos de DB. Además, no podrá usar métodos como ObjectContext.CreateObject (no podrá completar el objeto).

La plantilla Entity Object T4 y la generación de código predeterminada crean el método de fábrica CreateMyClass en lugar de constructor con parámetros. La plantilla POCO T4 no genera el método de fábrica.

No probé esto primero con el código EF.

Cuestiones relacionadas