2009-08-18 32 views
29

Tôi có một số lớp không thay đổi được khi giá trị ban đầu của chúng được đặt. Eric Lippert gọi số này là write-once immutability.Tính không biến đổi và tuần tự hóa XML

Thực hiện ghi một lần bất biến trong C# thường có nghĩa là đặt giá trị ban đầu thông qua hàm tạo. Các giá trị này khởi tạo các trường chỉ đọc.

Nhưng nếu bạn cần tuần tự hóa một lớp như thế này thành XML, sử dụng XmlSerializer hoặc DataContractSerializer, bạn phải có một hàm tạo tham số.

Có ai có đề xuất về cách khắc phục sự cố này không? Có những dạng bất biến nào khác hoạt động tốt hơn với serialization không?

EDIT: Như @Todd đã chỉ ra, DataContractSerializer không yêu cầu một hàm tạo parameterless. Theo số DataContractSerializer documentation on MSDN, DataContractSerializer "không gọi hàm khởi tạo của đối tượng đích."

+1

Tôi nghĩ rằng bạn đang gây nhầm lẫn ... những gì bạn mô tả là bất khả xâm phạm popsicle. Nó trông giống như viết một lần bất biến với tôi. –

+0

chỉnh sửa: ... là ** không ** popsicle ... –

+0

Bạn hoàn toàn đúng, @Martinho. Tôi đã sửa chữa câu hỏi của mình để đọc "viết một lần bất biến". – dthrasher

Trả lời

9

Giả sử đây là bạn "bất biến" đối tượng:

public class Immutable 
{ 
    public Immutable(string foo, int bar) 
    { 
     this.Foo = foo; 
     this.Bar = bar; 
    } 

    public string Foo { get; private set; } 
    public int Bar { get; private set; } 
} 

Bạn có thể tạo ra một lớp giả để bảo đảm rằng những đối tượng bất biến trong serialization/deserialization:

public class DummyImmutable 
{ 
    public DummyImmutable(Immutable i) 
    { 
     this.Foo = i.Foo; 
     this.Bar = i.Bar; 
    } 

    public string Foo { get; set; } 
    public int Bar { get; set; } 

    public Immutable GetImmutable() 
    { 
     return new Immutable(this.Foo, this.Bar); 
    } 
} 

Khi bạn có một loại tài sản của Không thể thay đổi, không tuần tự hóa nó, và thay vào đó tuần tự hóa một DummyImmutable:

[XmlIgnore] 
public Immutable SomeProperty { get; set; } 

[XmlElement("SomeProperty")] 
public DummyImmutable SomePropertyXml 
{ 
    get { return new DummyImmutable(this.SomeProperty); } 
    set { this.SomeProperty = value != null ? value.GetImmutable() : null; } 
} 

OK, đây là một chút dài cho một cái gì đó trông rất đơn giản ... nhưng nó sẽ làm việc;)

+2

Điều này thật khó chịu, nhưng dường như là cách tiếp cận được Microsoft khuyên dùng. –

8

"Realio-trulio" bất biến liên quan đến các nhà xây dựng. Popsicle tính bất biến là nơi bạn có thể làm, ví dụ:

Person p = new Person(); 
p.Name = "Fred"; 
p.DateOfBirth = DateTime.Today; 
p.Freeze(); // **now** immutable (edit attempts throw an exception) 

(hoặc tương tự với một initializer đối tượng)

này phù hợp DataContractSerializer khá tốt, miễn là bạn xử lý để trên serialized gọi lại để làm Freeze. XmlSerializerkhông làm các lần gọi lại tuần tự hóa, do đó, có nhiều công việc hơn.

Hoặc sẽ phù hợp nếu bạn sử dụng tùy chỉnh tuần tự hóa (IXmlSerializable). Tương tự, serialization tùy chỉnh là rộng rãi doable với realio-trulio bất biến, nhưng là đau đớn - và nó là một chút dối trá, vì nó là "tạo một lần, sau đó gọi phương thức giao diện" - vì vậy không thực sự đúng cách không thay đổi.

Đối với bất biến thực sự, hãy sử dụng DTO.

+0

Tính bất biến 'Realio-trulio' là gì? –

+1

@ChibuezeOpata cái gì đó là * thực sự * không thay đổi (ít nhất, không gian lận) - 'readonly', vv –

+0

Haha, cool, cảm ơn! :) –

0

Tôi vừa xem bài viết bạn đã liên kết đến. Trong thuật ngữ của mình, các đối tượng sử dụng các trường chỉ đọc được khởi tạo trong hàm tạo được gọi là "không thay đổi chỉ viết".

"Tính bất biến Popsicle" hơi khác một chút. Lippert đưa ra hai ví dụ về nơi nó sẽ hữu ích: deserialization (vấn đề bạn đang cố gắng giải quyết), và tham chiếu vòng tròn trong đó A và B cần được tạo độc lập nhưng A cần tham chiếu đến B và B tham chiếu đến A .

Hai cách rõ ràng hơn để đạt được kết quả này đã được đề cập ở đây (như tôi đã viết điều này, trên thực tế). Mô hình Thomas Levesque đề cập về cơ bản là "Builder" mô hình. Nhưng nó là khá khó sử dụng trong trường hợp này bởi vì bạn không cần phải đi từ Builder đến Immutable, mà còn từ Immutable to Builder để serialization/deserialization là đối xứng.

Vì vậy, mẫu "khóa", như đã đề cập bởi Marc Gravell, có vẻ hữu ích hơn. Tôi không quen thuộc với C# mặc dù, vì vậy tôi không chắc làm thế nào tốt nhất để thực hiện nó. Tôi đoán có lẽ một tài sản riêng tư như bị khóa. Sau đó, tất cả các phương thức getter cần kiểm tra rõ ràng đối tượng có bị khóa (hay còn gọi là "frozen"). Nếu vậy họ nên ném một ngoại lệ (đó là một lỗi thời gian chạy, bạn không thể thực thi bất biến popstick tại thời gian biên dịch).

+0

Cảm ơn, Todd. Tôi đã sửa chữa câu hỏi của mình để đọc tính bất biến "viết-một lần". Tôi sẽ điều tra bằng cách sử dụng một phương pháp Freeze với DataContractSerializer. – dthrasher

+0

Hãy xem câu trả lời khác (thứ hai) của tôi. Nếu DataContractSerializer có thể deserialize thành viên tư nhân thì bạn không nên cần "popsicle immutability" hoặc một Freeze() phương pháp ở tất cả. –

2

Tôi khuyên bạn nên xem thảo luận tại đây: Why XML-Serializable class need a parameterless constructor.

Bạn nên cân nhắc sử dụng DataContractSerializer. Theo như tôi có thể nói từ các tài liệu, điều này không yêu cầu một constructor parameterless, và có thể serialize/deserialize thành viên tư nhân.

+0

Bạn nói đúng, Todd. Theo MSDN, DataContractSerializer không gọi hàm tạo: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx – dthrasher

+0

Lưu ý rằng DataContractSerializer chỉ có sẵn từ .NET 3.5. Bạn không thể sử dụng nó với .NET 2.0 – Maxence

6

Nếu lớp học thực sự không thay đổi, chỉ cần sử dụng các trường chỉ đọc công khai được đánh dấu bằng các thuộc tính.

[DataContract()] 
public class Immutable 
{ 
     [DataMember(IsRequired=true)] 
     public readonly string Member; 

     public Immutable(string member) 
     { 
      Member = member; 
     } 
} 
+0

Ah, cái này! lĩnh vực công cộng! đã cố gắng để làm cho nó làm việc ra khỏi tài sản ... cảm ơn cho câu trả lời này underloved (có, tôi biết đó là 5 năm sau đó) –

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