2008-11-06 3 views
146

Piszę kod do serializacji Xml. Z poniższą funkcją.Dlaczego klasa XML-Serializable potrzebuje konstruktora bez parametrów

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

Jeśli argument jest instancją klasy bez konstruktora bez parametrów, wygeneruje wyjątek.

Unhandled Wyjątek: System.InvalidOperationException: CSharpConsole.Foo nie mogą być szeregowane ponieważ nie posiada konstruktora bez parametrów. w System.Xml.Serialization.TypeDesc.CheckSupported() w System.Xml.Serialization.TypeScope.GetTypeDesc (typ typu, MemberInfo Sourc e, Boolean directReference, Boolean throwOnError) w System.Xml.Serialization. ModelScope.GetTypeModel (typ typu Boolean bezpośredniego odniesienia) w System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping (typ typu, XmlRootAttribute korzeniowego, String defaultNamespace) w System.Xml.Serialization.XmlSerializer..ctor (typ type, String defaultName space) at System.Xml.Serialization.XmlSerializer..ctor (Typ )

Dlaczego musi istnieć konstruktor bez parametrów, aby umożliwić serializację XML, aby odnieść sukces?

EDYCJA: dzięki za odpowiedź cfeduke. Konstruktor bez parametrów może być prywatny lub wewnętrzny.

+1

Jeśli jesteś zainteresowany, znalazłem sposób tworzenia obiektów bez konieczności konstruktora (patrz aktualizacji) - ale to nie pomoże XmlSerializer w ogóle - to nadal wymaga. Przydatne może być dla niestandardowego kodu. –

+0

'XmlSerializer' wymaga domyślnego konstruktora bez parametrów do deserializacji. –

Odpowiedz

208

Podczas dekompresowania obiektu, klasa odpowiedzialna za usuwanie serializacji obiektu tworzy wystąpienie klasy serializowanej, a następnie kontynuuje wypełnianie serializowanych pól i właściwości dopiero po pobraniu instancji do zapełnienia.

Możesz utworzyć konstruktora private lub internal, jeśli chcesz, tak długo, jak to jest bez parametrów.

+0

Och, więc mogę sprawić, by ctor bez parametrów był prywatny lub wewnętrzny, a serializacja nadal działa. Dzięki za odpowiedź. –

+1

Tak, robię to często, chociaż doszedłem do wniosku, że publiczne konstruktory bez parametrów są świetne, ponieważ pozwalają na użycie "new()" z generycznymi i nową składnią inicjalizacyjną. W przypadku konstruktorów o określonych parametrach należy użyć statycznych metod fabrycznych lub implementacji wzorca budowniczego. – cfeduke

+11

Wskazówka dotycząca dostępności jest dobra, ale twoje wyjaśnienie nie ma sensu dla serializacji. Obiekt musi zostać utworzony tylko w celu odseparowania. Mogę zaryzykować przypuszczenie, że kod sprawdzania typu jest wbudowany w konstruktor XmlSerializer, ponieważ pojedyncza instancja może być używana w obie strony. –

0

Przede wszystkim to, co jest napisane w documentation. Myślę, że jest to jeden z twoich pól klasowych, a nie główny - i jak chcesz deserialiser, aby go zbudować z powrotem bez konstrukcji bez parametrów?

Myślę, że istnieje obejście, aby konstruktor był prywatny.

65

To jest ograniczenie do XmlSerializer. Zauważ, że wymagają tego: mogą utworzyć niezainicjowany obiekt z eteru i zainicjować go podczas deserializacji.

Ponieważ używasz xml, można rozważyć użycie DataContractSerializer i oznakowanie swoją klasę z [DataContract]/[DataMember], należy jednak pamiętać, że ta zmienia schemat (na przykład, nie ma odpowiednika [XmlAttribute] - wszystko staje się elementów).

Aktualizacja: jeśli naprawdę chcesz się dowiedzieć, BinaryFormatter i innych użyć FormatterServices.GetUninitializedObject(), aby utworzyć obiekt bez wywoływania konstruktora.Prawdopodobnie niebezpieczne; Nie polecam go używać zbyt często ;-P Zobacz także uwagi dotyczące MSDN:

Ponieważ nowa instancja obiektu jest inicjowany do zera i nie konstruktory są uruchamiane, obiekt może nie reprezentują stan uznany za ważny przez ten obiekt. Bieżąca metoda powinna być używana tylko do deserializacji , gdy użytkownik zamierza , aby natychmiast wypełnić wszystkie pola. To nie tworzy niezainicjowanej ciąg, ponieważ utworzenie pustej instancji typu niezmiennego służy bezcelowe.

Mam silnik do serializacji own, ale nie zamierzam go używać FormatterServices; Lubię wiedzieć, że konstruktor (dowolny konstruktor) rzeczywiście został wykonany.

+0

Dzięki za cynk o FormatterServices.GetUninitializedObject (typ). :) –

+4

Heh; okazuje się, że nie postępuję zgodnie z moją własną radą; protobuf-net ma (opcjonalnie) dozwolone użycie 'FormatterServices' dla * grup wiekowych * –

+1

Ale nie rozumiem, że w przypadku, gdy nie podano żadnego konstruktora, kompilator tworzy publiczny konstruktor bez parametrów. Dlaczego więc nie jest wystarczająco dobre dla silnika deserializacji xml? – toddmo