2012-02-23 19 views
9

DataContractSerializer no llama a un constructor o invocar inicializadores de campo al deserializar:¿Se puede inicializar un campo de solo lectura cuando se usa DataContractSerializer?

DataContractSerializer doesn't call my constructor?

Field Initializer in C# Class not Run when Deserializing

Setting the initial value of a property when using DataContractSerializer

¿Es posible inicializar un campo readonly después de deserialización objeto? ¿Debo abandonar esa característica de idioma para usar DataContractSerializer?

+1

¿Por qué no lo intentas? – svick

+0

@svick: No se me ocurre ningún mecanismo para realizar la iniciación. DataContractSerializer utiliza 'FormatterServices.GetUninitializedObject' para" construir "un objeto vacío y no invoca un constructor ni ejecuta inicializadores de campo. Por lo que entiendo, un campo 'readonly' solo se puede inicializar de una de esas dos maneras (pero espero que haya otra forma con la que no estoy familiarizado). –

+0

Oh, pensé que querías deserializar el campo.¿Le entiendo correctamente que, por ejemplo, desea establecer el campo en un valor predeterminado, que no proviene de los datos serializados? – svick

Respuesta

3

No estoy seguro de si esto es una buena idea, pero puede cambiar el valor de un campo readonly fuera del constructor o inicializador de campo mediante el uso de la reflexión.

Poner algo como:

typeof(MyType).GetField("Field").SetValue(this, value); 

en su devolución de llamada deserialización debería funcionar.

+0

Tenía ambos pensamientos también ... la reflexión probablemente funcionará, pero probablemente no sea una gran idea. Aún así, tal vez sea la única opción. –

2

Sí, usando DataContractSerializer puede serializar un campo readonly. Incluso puede serializar un campo que no sea publicreadonly.

using System; 
using System.Diagnostics; 
using System.IO; 
using System.Runtime.Serialization; 

namespace ConsoleApplication30 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Test a = new Test(1, 2); 
      Test b; 
      using (var ms = new MemoryStream()) 
      { 
       DataContractSerializer ser = new DataContractSerializer(typeof(Test)); 
       ser.WriteObject(ms, a); 
       ms.Position = 0; 
       b = (Test) ser.ReadObject(ms); 
      } 
      Trace.Assert(a.Data1 == b.Data1); 
      Trace.Assert(a.Data2 == b.Data2); 
     } 
    } 

    [DataContract] 
    public class Test 
    { 
     [DataMember] 
     public readonly int Data1; 

     [DataMember] 
     private readonly int _data2; 
     public int Data2 
     { 
      get { return _data2; } 
     } 

     public Test(int data1, int data2) 
     { 
      Data1 = data1; 
      _data2 = data2; 
     } 
    } 
} 
+1

El problema es que el objeto (que se diseñó ignorando el comportamiento de los serializadores específicos) inicializa el estado del objeto en el constructor y lo escribe en un campo 'readonly'. El contenido del campo 'readonly' no está destinado a ser serializado. No hay ninguna razón para serializar ese estado interno porque siempre se puede volver a crear, y conceptualmente es privado para la implementación de la clase. –

+1

@ EricJ.Serialization es [por definición] (https://en.wikipedia.org/wiki/Serialization) un proceso de traducción de estructuras de datos o estado del objeto. Es por eso que _es los campos de objeto que están destinados a ser serializados_, no las propiedades (sus valores siempre se vuelven a crear desde los campos) o los parámetros del constructor (no son necesariamente capturados por el estado del objeto). Si uno necesita recrear el objeto a partir de un conjunto de parámetros de constructor, este conjunto de parámetros de constructor debe serializarse en lugar de ser el objeto mismo. – Lightman

0

Encontré una manera limpia de lograr lo que busca sin romper su diseño.

Al usar este método, se asegurará de que se invoque su constructor y que se configure correctamente el campo de solo lectura.


Lo que se necesita es para serializar y deserializar [DataMember] campos marcados con son de una clase DataModel realidad.

Eso evitará que ocurra un comportamiento no deseado sabiendo que DataContractSerializer no llama al constructor al deserializar.

namespace MyApp.DataModel 
{ 
    //It is not mandatory to specify the DataContract since a default one will be 
    //provided on recent version of .Net, however it is a good practice to do so. 
    [DataContract(Name = "MyClassDataModel", Namespace = "MyApp.DataModel")] 
    public class MyClassDataModel 
    { 
     [DataMember] 
     public bool DataMemberExample{ get; set; } 
    } 

} 

Para deserializar y serializar, ahora puede usar esta clase para mantener sus valores.

Al cargar, puede crear un nuevo objeto de modelo de datos y pasarlo a su clase que necesita tener su constructor llamado.

public MyObjectThatNeedToBeConstructed LoadData(){ 
    // ... 
    // get your reader (Stream) from the file system or wherever it's located 
    var deserializer = new DataContractSerializer(typeof(MyClassDataModel)); 
    var storedModel = (MyClassDataModel)deserializer.ReadObject(reader); 

    return new MyObjectThatNeedToBeConstructed(storedModel); 
} 

Al guardar, puede extraer los datos requeridos de su clase que contienen el campo ReadOnly.

public void SaveData(MyObjectThatNeedToBeConstructed myObject){ 
    // ... 
    // get your writer (Stream) from memory or wherever it's located 
    var serializer = new DataContractSerializer(typeof(MyClassDataModel)); 
    var dataModel = new MyClassDataModel{ DataMemberExample = myObject.DataMember}; 
    serializer.WriteObject(writer, dataModel); 
} 

Por supuesto, usted tendrá que añadir una sobrecarga al constructor y es posible que tenga que ajustar un poco el ejemplo, pero creo que tienes la imagen.

Cuestiones relacionadas