XML Serileştirilebilir sınıfın neden parametresiz bir kurucuya ihtiyacı var?


173

Xml serileştirme yapmak için kod yazıyorum. Aşağıdaki fonksiyon ile.

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

Argüman parametresiz yapıcısı olmayan bir sınıf örneğiyse, bir istisna atar.

İşlenmeyen Özel Durum: System.InvalidOperationException: CSharpConsole.Foo parametresiz bir kurucuya sahip olmadığı için serileştirilemiyor. System.Xml.Serialization.TypeScope.GetTypeDesc'de (Type type, MemberInfo sourc e, Boolean directReference, Boolean throwOnError) at System.Xml.Serialization.ModelScope.GetTypeModel (Type, System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping'de (Type type, XmlRootAttribute root, String defaultNamespace) System.Xml.Serialization.XmlSerializer..ctor (Type type, Dize defaultName space) öğesinde System.Xml.Serialization.XmlSerializer..ctor (Type type, String defaultName space). XmlSerializer..ctor (Tür tipi)

Neden xml serileştirmenin başarılı olmasına izin vermek için parametresiz bir kurucu olmalı?

EDIT: cfeduke cevabı için teşekkürler. Parametresiz kurucu özel veya dahili olabilir.


1
İlgileniyorsanız, yapıcıya ihtiyaç duymadan nasıl nesne oluşturacağınızı buldum (güncellemeye bakın) - ancak bu XmlSerializer'a hiç yardımcı olmaz - yine de talep ediyor. Belki de özel kod için kullanışlıdır.
Marc Gravell

1
XmlSerializerserileştirme için varsayılan parametresiz bir kurucu gerektirir.
Amit Kumar Ghosh

Yanıtlar:


243

Bir nesnenin serileştirilmesi sırasında, bir nesnenin serileştirilmesinden sorumlu sınıf serileştirilmiş sınıfın bir örneğini oluşturur ve sonra serileştirilmiş alanları ve özellikleri yalnızca doldurmak için bir örnek aldıktan sonra doldurmaya devam eder.

Yapıcıyı privateya internalda isterseniz parametresiz olduğu sürece yapabilirsiniz.


1
Oh, bu yüzden parametresiz ctor'u özel veya dahili yapabilirim ve serileştirme hala çalışıyor. Cevabınız için teşekkürler.
Morgan Cheng

2
Evet sık sık yapıyorum, ancak genel parametresiz kurucuların mükemmel olduğunu kabul etmeye geldim çünkü jenerikler ve yeni başlatma sözdizimi ile "new ()" kullanmanıza izin veriyorlar. Parametreli kurucular için statik fabrika yöntemlerini veya kurucu kalıp uygulamasını kullanın.
cfeduke

14
Erişilebilirlik ipucu iyi bir ipucu, ancak açıklamanız serileştirme için bir anlam ifade etmiyor. Bir nesnenin sadece serileştirme için oluşturulması gerekir. Tek bir örnek her iki şekilde de kullanılabileceğinden, tip kontrol kodunun XmlSerializer yapıcısına yerleşik olduğunu tahmin ederim.
Tomer Gabel

7
@jwg Buna bir örnek, XML'inizi bir tür web servisine gönderdiğinizde ve bu nesneleri kendi bileşeninizde almakla ilgilenmemenizdir.
Tomer Gabel

5
Parametresiz kurucunuzu yapsanız privateveya internaldeğerleri serileştirilmiş tüm özelliklerinizin publicayarlayıcıları olması gerektiğini unutmayın .
chrnola

75

Bu bir sınırlamadır XmlSerializer. Unutmayın BinaryFormatterve DataContractSerializer bunu gerektirmezler - eterden başlatılmamış bir nesne oluşturabilir ve serileştirme sırasında başlatabilirler.

Xml kullandığınız DataContractSerializeriçin, sınıfınızı [DataContract]/ [DataMember] ile kullanmayı ve işaretlemeyi düşünebilirsiniz , ancak bunun şemayı değiştirdiğini unutmayın (örneğin, eşdeğeri yoktur [XmlAttribute]- her şey öğe olur).

Güncelleme: gerçekten bilmek istiyorsanız, yapıcıyı çağırmadan nesneyi oluşturmak için BinaryFormatterkullanın FormatterServices.GetUninitializedObject(). Muhtemelen tehlikeli; Çok sık kullanmamanızı tavsiye etmiyorum ;-p MSDN'deki açıklamalara da bakınız:

Nesnenin yeni örneği sıfır olarak başlatıldığından ve hiçbir kurucu çalıştırılmadığından, nesne bu nesne tarafından geçerli kabul edilen bir durumu temsil etmeyebilir. Geçerli yöntem, yalnızca kullanıcı derhal tüm alanları doldurmak istediğinde serileştirme için kullanılmalıdır. Başlatılmayan bir dize oluşturmaz, çünkü değişmez tipte boş bir örnek oluşturmak hiçbir amaca hizmet etmez.

Benim var kendi seri hale motoru, ama kullanmak yapma niyetinde değilim FormatterServices; Bir kurucunun ( herhangi bir kurucu) gerçekten yürüttüğünü bilmek hoşuma gidiyor .


FormatterServices.GetUninitializedObject (Type) hakkındaki ipucu için teşekkür ederiz. :)
Omer van Kloeten

6
Heh; kendi tavsiyeme uymadığım ortaya çıkıyor; protobuf-net (isteğe bağlı olarak) yaşlarFormatterServices için kullanıma izin verdi
Marc Gravell

1
Ama anlamadığım şey, hiçbir kurucu belirtilmemişse, derleyici genel parametresiz bir kurucu oluşturur. Peki bu neden xml serileştirme motoru için yeterince iyi değil?
toddmo

XML serisini kaldırmak ve yapıcısını kullanarak belirli bir nesne başlatmak istiyorsanız (böylece öğeleri / öznitelikleri kurucu aracılığıyla sağlanır), bunu başarmak için herhangi bir yolu var mı? Serileştirme sürecini, yapıcılarını kullanarak nesneleri oluşturacak şekilde özelleştirmenin bir yolu yok mu?
Shimmy Weitzhandler

1
@Shimmy hayır; bu desteklenmiyor. Orada olduğunu IXmlSerializable ama: olur sonra yapıcı ve b: bunun doğru (özellikle deserialization) almak çok çirkin ve zor - şiddetle bu uygulamaya çalışırken karşı tavsiye ama: Eğer kurucular kullanmasına izin vermez
Marc Gravell

4

Cevap: hiçbir şekilde iyi bir sebep yok.

İsminin aksine, XmlSerializersınıf sadece serileştirme için değil, aynı zamanda serileştirme için de kullanılır. Sınıfınızın çalışacağından emin olmak için belirli kontroller yapar ve bu kontrollerden bazıları sadece serileştirme ile ilgilidir, ancak yine de hepsini yapar, çünkü daha sonra ne yapmak istediğinizi bilmez.

Sınıfınızın geçemediğini kontrol etmek, sadece serileştirme ile ilgili kontrollerden biridir. İşte olanlar:

  • Serileştirme sırasında XmlSerializersınıfın türünüzün örneklerini oluşturması gerekir.

  • Bir türün örneğini oluşturmak için, o türdeki bir kurucunun çağrılması gerekir.

  • Bir kurucu bildirmediyseniz, derleyici zaten varsayılan parametresiz bir kurucu sağlamıştır, ancak bir kurucu bildirdiyseniz, kullanılabilir tek kurucu budur.

  • Yani, bildirdiğiniz yapıcı parametreleri kabul ederse, sınıfınızı başlatmanın tek yolu parametreleri kabul eden yapıcıyı çağırmaktır.

  • Bununla birlikte, XmlSerializerparametresiz bir kurucu dışında herhangi bir kurucuyu çağıramaz, çünkü parametreleri kabul eden kuruculara hangi parametrelerin aktarılacağını bilmez. Bu nedenle, sınıfınızda parametresiz bir kurucu olup olmadığını kontrol eder ve olmadığından başarısız olur.

Yani, eğer XmlSerializersınıf sadece serileştirme ile ilgili kontrolleri yapacak şekilde yazılmış olsaydı, o zaman sınıfınız geçecekti, çünkü serileştirme hakkında parametresiz bir kurucuya sahip olmayı zorunlu kılan hiçbir şey yoktur.

Diğerlerinin de belirttiği gibi, probleminizin hızlı çözümü sadece parametresiz bir kurucu eklemektir. Ne yazık ki, aynı zamanda kirli bir çözümdür, çünkü readonlyyapıcı parametrelerinden başlatılan üyeleriniz olamaz .

Bütün bunlara ek olarak, XmlSerializersınıf olabilir parametresiz yapıcıları olmadan sınıfların bile deserialization izin verecek şekilde yazılmıştır. Tek yapmanız gereken "Fabrika Metodu Tasarım Deseni" ni (Wikipedia) kullanmak olacaktır . Görünüşe göre Microsoft, bu tasarım deseninin, gereksiz yere bu tür şeylerle karıştırılmaması gereken DotNet programcıları için çok ileri olduğuna karar verdi. Bu nedenle, Microsoft'a göre DotNet programcıları parametresiz kuruculara daha iyi bağlı kalmalıdır.


Lol diyorsun, For no good reason whatsoever,sonra bir kurucuya 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.hangi parametrelerin geçeceğini bilmiyorsa, o zaman hangi parametrelerin bir fabrikaya geçeceğini nasıl bilebilirdi? Veya hangi fabrika kullanılacak? Bu aracın kullanımı daha kolay olduğunu düşünemiyorum - bir sınıfın serileştirilmesini istiyorsunuz, ardından serileştiricinin varsayılan bir örnek oluşturmasını ve ardından etiketlediğiniz her alanı doldurmasını istiyorsunuz. Kolay.
Chuck

0

Her şeyden önce, belgelerde yazılı olan budur . Bence sınıf alanlarından biri değil, ana alan - ve deserialiser onu parametresiz inşaat olmadan geri inşa etmek istediğiniz nasıl?

Bence yapıcıyı özel yapmak için bir çözüm var.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.