2012-02-23 18 views
9

DataContractSerializer không gọi một constructor hoặc gọi initializers trường khi deserializing:Trường có thể đọc được khởi tạo khi sử dụng DataContractSerializer không?

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

Có thể khởi tạo một lĩnh vực readonly sau khi đối tượng deserialization? Tôi có phải từ bỏ tính năng ngôn ngữ đó để sử dụng DataContractSerializer không?

+1

Tại sao bạn không thử? – svick

+0

@svick: Tôi không thể nghĩ ra bất kỳ cơ chế nào để thực hiện việc gây quỹ. DataContractSerializer sử dụng 'FormatterServices.GetUninitializedObject' để" xây dựng "một đối tượng rỗng và không gọi một hàm tạo hoặc khởi tạo trường khởi tạo. Theo như tôi hiểu, một trường 'readonly' chỉ có thể được khởi tạo theo một trong hai cách đó (nhưng hy vọng có một cách khác mà tôi không quen thuộc). –

+0

Ồ, tôi nghĩ bạn muốn deserialize lĩnh vực này.Tôi có hiểu bạn đúng không, ví dụ bạn muốn đặt trường thành một số giá trị mặc định, không đến từ dữ liệu được tuần tự hóa? – svick

Trả lời

3

Tôi không chắc đây là một ý hay hay không, nhưng bạn có thể thay đổi giá trị của trường readonly bên ngoài hàm khởi tạo hoặc trình khởi tạo trường bằng cách sử dụng phản chiếu.

Đưa cái gì đó như:

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

trong deserialization callback của bạn nên làm việc.

+0

Tôi đã có cả hai suy nghĩ cũng ... phản ánh có thể sẽ làm việc, nhưng nó có lẽ không phải là một ý tưởng tuyệt vời. Tuy nhiên, có lẽ đó là lựa chọn duy nhất. –

2

Có, sử dụng DataContractSerializer bạn có thể tuần tự hóa trường readonly. Bạn thậm chí có thể tuần tự hóa một trường không phải là 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

Vấn đề là đối tượng (được thiết kế trong sự thiếu hiểu biết về hành vi của các trình tuần tự cụ thể) khởi tạo trạng thái đối tượng trong hàm tạo và viết trạng thái đó vào trường 'readonly'. Nội dung của trường 'readonly' không được sắp xếp theo thứ tự. Không có lý do gì để tuần tự hóa trạng thái bên trong vì nó luôn có thể được tạo lại, và khái niệm là riêng tư đối với việc thực thi lớp đó. –

+1

@ EricJ.Serialization là [theo định nghĩa] (https://en.wikipedia.org/wiki/Serialization) một quá trình dịch cấu trúc dữ liệu hoặc trạng thái đối tượng. Đó là lý do tại sao _it là các trường đối tượng được dự định được serialized_, không phải các thuộc tính (giá trị của chúng luôn được tạo lại từ các trường) hoặc các tham số hàm tạo (không nhất thiết phải được ghi lại bởi trạng thái đối tượng). Nếu một trong những nhu cầu để tạo lại đối tượng từ một tập hợp các tham số constructor, tập hợp các tham số constructor nên được tuần tự hóa thay vào đó, không phải đối tượng chính nó. – Lightman

0

Tôi đã tìm thấy một cách rõ ràng để đạt được những gì bạn đang tìm kiếm mà không phá vỡ thiết kế của bạn.

Sử dụng phương pháp này sẽ đảm bảo rằng hàm tạo của bạn sẽ được gọi và trường chỉ đọc của bạn được đặt chính xác.


Những gì bạn cần là thực sự tuần tự hóa và deserialize [DataMember] các trường được đánh dấu từ lớp DataModel.

Điều đó sẽ ngăn chặn bất kỳ hành vi ngoài ý muốn nào xảy ra khi biết rằng DataContractSerializer không gọi hàm tạo khi deserializing.

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; } 
    } 

} 

Để deserializing và serializing bây giờ bạn có thể sử dụng lớp này để giữ giá trị của bạn.

Khi tải, bạn có thể tạo một đối tượng mô hình dữ liệu mới và chuyển nó tới lớp của bạn cần phải có hàm tạo của nó.

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); 
} 

Khi lưu, bạn có thể trích xuất dữ liệu bắt buộc từ lớp có chứa trường 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); 
} 

Tất nhiên, bạn sẽ phải thêm quá tải vào hàm tạo và bạn có thể phải chỉnh sửa ví dụ một chút, nhưng tôi nghĩ bạn có ảnh.

Các vấn đề liên quan