Tại sao lớp XML-serializable cần một hàm tạo không tham số


173

Tôi đang viết mã để thực hiện tuần tự hóa Xml. Với chức năng dưới đây.

public static string SerializeToXml(object obj)
{
    XmlSerializer serializer = new XmlSerializer(obj.GetType());
    using (StringWriter writer = new StringWriter())
    {
        serializer.Serialize(writer, obj);
        return writer.ToString();
    }
}

Nếu đối số là một thể hiện của lớp không có hàm tạo không tham số, nó sẽ đưa ra một ngoại lệ.

Ngoại lệ chưa được xử lý: System.InvalidOperationException: CSharpConsole.Foo không thể được tuần tự hóa vì nó không có hàm tạo không tham số. tại System.Xml.Serialization.TypeDesc.CheckSupported () tại System.Xml.Serialization.TypeScope.GetTypeDesc (Type type, MemberInfo sourc e, Boolean directReference, Boolean throwOnError) tại System.Xodel Tham chiếu trực tiếp Boolean) tại System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (Kiểu loại, gốc XmlRootAttribution, String defaultNamespace) tại System.Xml.Serialization.XmlSerializer..ctor (Loại, mặc định. XmlSerializer..ctor (Loại loại)

Tại sao phải có một hàm tạo không tham số để cho phép xml serialization thành công?

EDIT: cảm ơn câu trả lời của cfeduke. Các constructor tham số có thể là riêng tư hoặc nội bộ.


1
Nếu bạn quan tâm, tôi đã tìm cách tạo các đối tượng mà không cần đến hàm tạo (xem cập nhật) - nhưng điều này sẽ không giúp ích gì cho XmlSerializer - nó vẫn đòi hỏi điều đó. Hữu ích cho mã tùy chỉnh, có thể.
Marc Gravell

1
XmlSerializeryêu cầu một hàm tạo không tham số mặc định để khử lưu huỳnh.
Amit Kumar Ghosh

Câu trả lời:


243

Trong quá trình khử tuần tự hóa của một đối tượng, lớp chịu trách nhiệm khử tuần tự hóa một đối tượng sẽ tạo ra một thể hiện của lớp được tuần tự hóa và sau đó tiến hành cư trú các trường và các thuộc tính chỉ sau khi có được một thể hiện để cư trú.

Bạn có thể tạo hàm tạo của mình privatehoặc internalnếu bạn muốn, miễn là nó không tham số.


1
Ồ, vì vậy, tôi có thể làm cho ctor tham số riêng tư hoặc nội bộ và serialization vẫn hoạt động. Cảm ơn câu trả lời của bạn.
Morgan Cheng

2
Có, tôi thường xuyên làm điều đó, mặc dù tôi đã chấp nhận rằng các hàm tạo không tham số công khai là tuyệt vời vì chúng cho phép bạn sử dụng "new ()" với generic và cú pháp khởi tạo mới. Đối với các nhà xây dựng tham số sử dụng các phương thức nhà máy tĩnh hoặc triển khai mẫu xây dựng.
cfeduke

14
Mẹo tiếp cận là một cách tốt, nhưng lời giải thích của bạn không có ý nghĩa cho việc tuần tự hóa. Một đối tượng cần được tạo ra chỉ để khử tuần tự. Tôi muốn đoán rằng mã kiểm tra loại được tích hợp vào hàm tạo XmlSerializer vì một trường hợp duy nhất có thể được sử dụng theo cả hai cách.
Tomer Gabel

7
@jwg Một ví dụ là khi bạn gửi XML của mình đến một dịch vụ web nào đó và không quan tâm đến việc nhận các đối tượng đó trong thành phần của riêng bạn.
Tomer Gabel

5
Hãy nhớ rằng ngay cả khi bạn tạo hàm tạo không tham số privatehoặc internaltất cả các thuộc tính có giá trị được tuần tự hóa phải có publicsetters.
chrnola

75

Đây là một hạn chế của XmlSerializer. Lưu ý rằng BinaryFormatterDataContractSerializer không yêu cầu điều này - họ có thể tạo ra một đối tượng chưa được khởi tạo từ ether và khởi tạo nó trong quá trình khử lưu huỳnh.

Vì bạn đang sử dụng xml, bạn có thể cân nhắc sử dụng DataContractSerializervà đánh dấu lớp của mình bằng [DataContract]/ [DataMember], nhưng lưu ý rằng điều này thay đổi lược đồ (ví dụ: không có tương đương [XmlAttribute]- mọi thứ trở thành các phần tử).

Cập nhật: nếu bạn thực sự muốn biết, BinaryFormatteret al sử dụng FormatterServices.GetUninitializedObject()để tạo đối tượng mà không cần gọi hàm tạo. Có lẽ nguy hiểm; Tôi không khuyên bạn nên sử dụng nó quá thường xuyên ;-p Xem thêm các nhận xét về MSDN:

Vì phiên bản mới của đối tượng được khởi tạo về 0 và không có hàm tạo nào được chạy, nên đối tượng có thể không đại diện cho trạng thái được coi là hợp lệ bởi đối tượng đó. Phương pháp hiện tại chỉ nên được sử dụng để khử lưu huỳnh khi người dùng dự định cư trú ngay lập tức tất cả các trường. Nó không tạo ra một chuỗi chưa được khởi tạo, vì việc tạo một thể hiện trống của một kiểu bất biến không phục vụ mục đích nào.

Tôi có công cụ tuần tự hóa của riêng mình , nhưng tôi không có ý định sử dụng nó FormatterServices; Tôi khá muốn biết rằng một constructor ( bất kỳ constructor) đã thực sự thực thi.


Cảm ơn các mẹo về FormatterService.GetUninitializedObject (Loại). :)
Omer van Kloeten

6
Heh; Hóa ra tôi không làm theo lời khuyên của chính mình; protobuf-net đã (tùy chọn) cho phép FormatterServicessử dụng cho các lứa tuổi
Marc Gravell

1
Nhưng điều tôi không hiểu là, trong trường hợp không có hàm tạo nào được chỉ định, trình biên dịch sẽ tạo một hàm tạo không tham số công khai. Vậy tại sao điều đó không đủ tốt cho công cụ khử lưu lượng xml?
chập chững

Nếu tôi muốn giải tuần tự hóa XML và khởi tạo một số đối tượng nhất định bằng cách sử dụng hàm tạo của chúng (để các phần tử / thuộc tính được cung cấp thông qua hàm tạo), có cách nào để đạt được điều này không? Không có cách nào để tùy chỉnh quá trình tuần tự hóa để nó xây dựng các đối tượng bằng cách sử dụng các hàm tạo của chúng?
Shimmy Weitzhandler

1
@Shimmy không; Điều đó không được hỗ trợ. Có IXmlSerializable , nhưng một: điều đó xảy ra sau khi các nhà xây dựng, và b: nó là rất xấu xí và khó khăn để có được quyền (đặc biệt là deserialization) - Tôi khuyên chống lại cố gắng để thực hiện điều đó, nhưng: nó sẽ không cho phép bạn sử dụng nhà thầu
Marc Gravell

4

Câu trả lời là: không có lý do chính đáng nào.

Trái ngược với tên của nó, XmlSerializerlớp không chỉ được sử dụng để tuần tự hóa mà còn cho quá trình khử lưu huỳnh. Nó thực hiện một số kiểm tra nhất định trên lớp của bạn để đảm bảo rằng nó sẽ hoạt động và một số kiểm tra đó chỉ phù hợp với quá trình khử lưu huỳnh, nhưng dù sao thì nó cũng thực hiện tất cả, vì nó không biết bạn dự định làm gì sau này.

Kiểm tra mà lớp của bạn không vượt qua là một trong những kiểm tra chỉ phù hợp với quá trình khử lưu huỳnh. Đây là những gì xảy ra:

  • Trong quá trình khử lưu huỳnh, XmlSerializerlớp sẽ cần tạo các thể hiện của kiểu của bạn.

  • Để tạo một thể hiện của một kiểu, một hàm tạo của kiểu đó cần phải được gọi.

  • Nếu bạn không khai báo hàm tạo, trình biên dịch đã cung cấp hàm tạo không tham số mặc định, nhưng nếu bạn đã khai báo hàm tạo, thì đó là hàm tạo duy nhất khả dụng.

  • Vì vậy, nếu hàm tạo mà bạn đã khai báo chấp nhận tham số, thì cách duy nhất để khởi tạo lớp của bạn là bằng cách gọi hàm tạo đó chấp nhận tham số.

  • Tuy nhiên, XmlSerializerkhông có khả năng gọi bất kỳ hàm tạo nào ngoại trừ hàm tạo không tham số, vì nó không biết tham số nào sẽ truyền cho các hàm tạo chấp nhận tham số. Vì vậy, nó kiểm tra xem lớp của bạn có một hàm tạo không tham số không, và vì nó không có, nên nó thất bại.

Vì vậy, nếu XmlSerializerlớp đã được viết theo cách chỉ thực hiện các kiểm tra phù hợp với tuần tự hóa, thì lớp của bạn sẽ vượt qua, bởi vì hoàn toàn không có gì về tuần tự hóa mà cần phải có một hàm tạo không tham số.

Như những người khác đã chỉ ra, giải pháp nhanh chóng cho vấn đề của bạn là chỉ cần thêm một hàm tạo không tham số. Thật không may, nó cũng là một giải pháp bẩn, bởi vì điều đó có nghĩa là bạn không thể có bất kỳ readonlythành viên nào được khởi tạo từ các tham số của hàm tạo.

Ngoài tất cả điều này, XmlSerializerlớp có thể đã được viết theo cách cho phép thậm chí khử lưu lượng các lớp mà không cần các hàm tạo không tham số. Tất cả những gì cần có là sử dụng "Mẫu thiết kế phương pháp nhà máy" (Wikipedia) . Từ vẻ bề ngoài của nó, Microsoft đã quyết định rằng mẫu thiết kế này quá tiên tiến đối với các lập trình viên DotNet, những người dường như không nên nhầm lẫn một cách không cần thiết với những thứ như vậy. Vì vậy, các lập trình viên DotNet nên bám vào các nhà xây dựng không tham số, theo Microsoft.


Bạn nói, For no good reason whatsoever,sau đó tiếp tục nói, XmlSerializer is not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters.Nếu nó không biết tham số nào để truyền cho một nhà xây dựng, thì làm sao nó biết được tham số nào để truyền cho nhà máy? Hoặc sử dụng nhà máy nào? Tôi không thể tưởng tượng công cụ này sẽ dễ sử dụng hơn - bạn muốn một lớp được giải tuần tự hóa, sau đó để trình giải nén tạo một thể hiện mặc định và sau đó điền vào từng trường bạn đã gắn thẻ. Dễ dàng.
Chuck

0

Trước hết, đây là những gì được viết trong tài liệu . Tôi nghĩ rằng đó là một trong các trường lớp của bạn, không phải là trường chính - và làm thế nào bạn muốn trình giải mã để xây dựng lại nó với cấu trúc không tham số?

Tôi nghĩ rằng có một cách giải quyết để làm cho nhà xây dựng riêng tư.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.