2012-02-23 12 views
9

DataContractSerializer एक निर्माता फोन नहीं करता है या क्षेत्र initializers आह्वान जब deserializing:DataContractSerializer का उपयोग करते समय एक पाठक फ़ील्ड प्रारंभ किया जा सकता है?

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

यह वस्तु अक्रमांकन के बाद एक readonly क्षेत्र को प्रारंभ करना संभव है? DataContractSerializer का उपयोग करने के लिए मुझे उस भाषा सुविधा को छोड़ देना चाहिए?

+1

आप इसे क्यों नहीं देखते? – svick

+0

@ एसविक: मैं इनटाइलाइजेशन करने के लिए किसी भी तंत्र के बारे में नहीं सोच सकता। DataContractSerializer एक खाली ऑब्जेक्ट को "निर्माण" करने के लिए 'FormatterServices.GetUninitializedObject' का उपयोग करता है और किसी कन्स्ट्रक्टर या रन फील्ड प्रारंभकर्ताओं को नहीं चलाता है। जहां तक ​​मैं समझता हूं, एक 'पठनीय' फ़ील्ड केवल उन दो तरीकों में से एक में शुरू किया जा सकता है (लेकिन उम्मीद है कि एक और तरीका है जिसे मैं परिचित नहीं हूं)। –

+0

ओह, मैंने सोचा था कि आप मैदान को deserialize करना चाहता था।क्या मैं आपको सही ढंग से समझता हूं कि उदाहरण के लिए आप फ़ील्ड को कुछ डिफ़ॉल्ट मान पर सेट करना चाहते हैं, जो धारावाहिक डेटा से नहीं आता है? – svick

उत्तर

3

मुझे यकीन नहीं है कि यह एक अच्छा विचार है, लेकिन आप प्रतिबिंब का उपयोग कर निर्माता या फील्ड प्रारंभकर्ता के बाहर readonly फ़ील्ड का मान बदल सकते हैं। अपने अक्रमांकन कॉलबैक काम करना चाहिए में

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

:

की तरह कुछ लाना।

+0

मेरे पास दोनों विचार भी थे ... प्रतिबिंब शायद काम करेगा, लेकिन शायद यह एक अच्छा विचार नहीं है। फिर भी, शायद यह एकमात्र विकल्प है। –

2

हाँ, DataContractSerializer का उपयोग कर आप एक readonly क्षेत्र को क्रमानुसार कर सकते हैं। आप एक गैर-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

समस्या यह है कि ऑब्जेक्ट (जो विशिष्ट धारावाहिकों के व्यवहार की अज्ञानता में डिज़ाइन किया गया था) कन्स्ट्रक्टर में ऑब्जेक्ट स्टेटस प्रारंभ करता है, और उस स्थिति को 'रीडोनली' फ़ील्ड में लिखता है। 'रीडोनली' फ़ील्ड की सामग्री स्वयं क्रमबद्ध नहीं है। उस आंतरिक राज्य को क्रमबद्ध करने का कोई कारण नहीं है क्योंकि इसे हमेशा फिर से बनाया जा सकता है, और अवधारणा कक्षा के कार्यान्वयन के लिए निजी है। –

+1

@ एरिकजे। क्रमबद्धता [परिभाषा के अनुसार] (https://en.wikipedia.org/wiki/Serialization) डेटा संरचनाओं या ऑब्जेक्ट स्थिति का अनुवाद करने की प्रक्रिया है। यही कारण है कि _it ऑब्जेक्ट फ़ील्ड है जिसका उद्देश्य serialized_ होना है, गुण नहीं (उनके मान हमेशा फ़ील्ड से बनाए जाते हैं) या कन्स्ट्रक्टर पैरामीटर (ऑब्जेक्ट स्टेटस द्वारा जरूरी नहीं हैं)। यदि किसी को कन्स्ट्रक्टर पैरामीटर के सेट से ऑब्जेक्ट को फिर से बनाने की आवश्यकता है, तो कन्स्ट्रक्टर पैरामीटर के इस सेट को इसके बजाय क्रमबद्ध किया जाना चाहिए, न कि ऑब्जेक्ट करें। – Lightman

0

मुझे आपके डिज़ाइन को तोड़ने के बिना आप जो खोज रहे हैं उसे प्राप्त करने का एक साफ तरीका मिला।

इस विधि का उपयोग सुनिश्चित करेगा कि आपके निर्माता के नाम से जाना जाएगा और अपने केवल पढ़ने के लिए क्षेत्र ठीक से सेट।


आप क्या जरूरत है वास्तव में क्रमानुसार और एक डेटामॉडल वर्ग से [DataMember] चिह्नित क्षेत्रों deserialize है।

यह किसी भी अनपेक्षित व्यवहार को यह जानने के लिए रोक देगा कि डेटाकंट्रैक्टसेरियलाइज़र 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 और serializing के लिए अब आप इस वर्ग का उपयोग अपने मूल्यों को पकड़ने के लिए कर सकते हैं।

लोड होने पर आप एक नया डेटा मॉडल ऑब्जेक्ट बना सकते हैं और इसे अपनी कक्षा में पास कर सकते हैं जिसके लिए इसके कन्स्ट्रक्टर को कॉल करने की आवश्यकता है।

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

बचत आप अपने वर्ग से आवश्यक डेटा कि 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); 
} 
बेशक

, आप निर्माता के लिए एक अधिभार जोड़ना होगा और आप उदाहरण थोड़ा tweak करना पड़ सकता है, लेकिन मुझे लगता है कि आप चित्र मिल गया।