2012-02-23 17 views
23

Deserializing जब मेरे पास एक संरक्षित फ़ील्ड को परिभाषित करता है। संरक्षित क्षेत्र में फ़ील्ड प्रारंभकर्ता है।सी # कक्षा में फ़ील्ड इनिशिलाइज़र

जब मैं ठोस वर्ग को deserialize, फ़ील्ड प्रारंभकर्ता नहीं चलाया जाता है। क्यूं कर? समस्या को हल करने के लिए सबसे अच्छा पैटर्न क्या है? अगर मैं एक निर्माता में प्रारंभिक स्थानांतरित करता हूं, तो कन्स्ट्रक्टर भी नहीं बुलाया जाता है।

[DataContract] 
public class MyConcrete 
{ 
    // FIELD INITIALIZER DOES NOT RUN WHEN COMMENTED IN: 
    protected readonly Dictionary<int, string> myDict;// = new Dictionary<int, string>(); 

    public MyConcrete() 
    { 
     myDict = new Dictionary<int, string>(); 
    } 

    private bool MyMethod(int key) 
    { 
     return myDict.ContainsKey(key); 
    } 

    private int myProp; 

    [DataMember] 
    public int MyProp 
    { 
     get { return myProp; } 
     set { bool b = MyMethod(value); myProp = value; } // Call MyMethod to provoke error 
    } 
} 

मूल वर्ग पदानुक्रम

[DataContract] 
public abstract class MyAbstract 
{ 
    // THIS INITIALIZER IS NOT RUN WHILE DESERIALIZING: 
    protected readonly Dictionary<int, string> myDict = new Dictionary<int, string>(); 

    private bool MyMethod(int key) 
    { 
     return myDict.ContainsKey(key); 
    } 

    private int myProp; 

    [DataMember] 
    public int MyProp 
    { 
     get { return myProp; } 
     set { bool b = MyMethod(value); myProp = value; } // Call MyMethod to provoke error 
    } 
} 

[DataContract] 
public class MyConcrete : MyAbstract 
{ 

} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     string tempfn = Path.GetTempFileName(); 

     MyConcrete concrete = new MyConcrete() { MyProp = 42 }; 
     string data = concrete.SerializeToString<MyConcrete>(); 

     MyConcrete rehydrated = SerializationHelper.DeserializeFromString<MyConcrete>(data); 
    } 
} 

सहायक विधि

static public string SerializeToString<T>(this T obj) 
{ 
    return SerializationHelper.SerializeToString<T>(obj); 
} 

static public string SerializeToString<T>(T obj) 
{ 
    DataContractSerializer s = new DataContractSerializer(typeof(T)); 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     s.WriteObject(ms, obj); 
     ms.Position = 0; 
     using (StreamReader sr = new StreamReader(ms)) 
     { 
      string serialized = sr.ReadToEnd(); 
      return serialized; 
     } 
    }    
} 

static public T DeserializeFromString<T>(string serializedDataAsString) 
{ 
    DataContractSerializer s = new DataContractSerializer(typeof(T)); 
    using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(serializedDataAsString))) 
    { 
     object s2 = s.ReadObject(ms); 
     return (T)s2; 
    } 
} 
+1

अमूर्त में संरक्षित कन्स्ट्रक्टर जोड़ने का प्रयास किया जहां आप dict प्रारंभ करते हैं? MyConcrete में एक सार्वजनिक कन्स्ट्रक्टर प्रदान करें जो 'आधार() 'तक चेन करता है। – Will

+0

मैंने इसे सिर्फ एक वर्ग में फटकार दिया और एक कन्स्ट्रक्टर जोड़ा। कन्स्ट्रक्टर का आह्वान नहीं किया जा रहा है। इस संबंधित पोस्ट को मिला है कि दावा करता है कि फील्ड प्रारंभकर्ता और कन्स्ट्रक्टरों का आह्वान नहीं किया जाता है ... http://stackoverflow.com/questions/5021973/constructors-not-called-on-deserialization –

उत्तर

39

अक्रमांकन neither the constructors nor the field initializers are called और एक "खाली" गैर-प्रारंभ वस्तु पर बजाय प्रयोग किया जाता है।

यह हल करने के लिए आप की OnDeserializing या OnDerserialized deserializer निम्नलिखित हस्ताक्षर के साथ एक समारोह कॉल करने के लिए जिम्मेदार बताते हैं उपयोग कर सकते हैं:

void OnDeserializing(System.Runtime.Serialization.StreamingContext c); 

कि समारोह में है, जहां आप प्रारंभ कर सकते हैं जो कुछ भी अक्रमांकन भीतर याद किया गया था प्रक्रिया।

सम्मेलन के मामले में, मुझे लगता है कि मेरा कन्स्ट्रक्टर एक विधि OnCreated() पर कॉल करता है और उसके बाद deserializating विधि भी वही बात कॉल करता है। फिर आप वहां के सभी फील्ड प्रारंभिकरण को संभाल सकते हैं और सुनिश्चित कर सकते हैं कि इसे deserialization से पहले निकाल दिया गया है।

[DataContract] 
public abstract class MyAbstract 
{ 
    protected readonly Dictionary<int, string> myDict; 

    protected MyAbstract() 
    { 
     OnCreated(); 
    } 

    private void OnCreated() 
    { 
     myDict = new Dictionary<int, string>(); 
    } 

    [OnDeserializing] 
    private void OnDeserializing(StreamingContext c) 
    { 
     OnCreated(); 
    } 

    private bool MyMethod(int key) 
    { 
     return myDict.ContainsKey(key); 
    } 

    private int myProp; 

    [DataMember] 
    public int MyProp 
    { 
     get { return myProp; } 
     set { bool b = MyMethod(value); myProp = value; } 
    } 
} 
+3

मैं OnDeserializing() विधि से एक पठनीय फ़ील्ड असाइन नहीं कर सकता ... serializer के लिए यह डिज़ाइन पसंद केवल पढ़ने के लिए उपयोग की क्षमता को तोड़ देता है ... फिर भी, सबसे अच्छा/केवल दृष्टिकोण प्रतीत होता है। –

+0

ओह हाँ ... आप केवल पढ़ने के बारे में सही हैं। Woops, उस चूक गए! – Reddog

+0

@Reddog +1 इस समाधान के लिए धन्यवाद, मैं बिना पढ़ाई के रह सकता हूं – surfen

8

एक और दृष्टिकोण एक संरक्षित (अपने उदाहरण में) संपत्ति के माध्यम से अपने क्षेत्र का उपयोग, और null-coalescing (??) ऑपरेटर

protected Dictionary<int, string> myDict = new Dictionary<int, string>(); 

protected Dictionary<int, string> MyDict 
{ 
    get 
    { 
     return myDict ?? (myDict = new Dictionary<int, string>()); 
    } 
} 

कमियां है कि आप readonly का लाभ खो रहे हैं का उपयोग कर क्षेत्र आरंभ करने के लिए है, और यह सुनिश्चित करने की आवश्यकता है कि आप केवल संपत्ति के माध्यम से मूल्य का उपयोग करें।

+0

+1, * readonly * के समान अर्थशास्त्र प्रदान करता है जब तक कि कक्षा में काम करने वाले सभी डेवलपर्स 'myDict' के बजाय' MyDict' तक पहुंचने के सम्मेलन को समझते हैं। कहीं ऐसा बग होने का कारण होगा, लेकिन कुछ परिस्थितियों के लिए उपयुक्त विकल्प। –

+1

इससे आपको कक्षा में आलसी आ जाएगी - केवल पढ़ने योग्य गुणों के लिए यह अच्छा है। – EvgeniyK

+0

फील्ड प्रारंभकर्ता को पूरी तरह से क्यों नहीं हटाएं?जब तक आपको इसकी आवश्यकता न हो तब तक शब्दकोश बनाने का कोई कारण नहीं है। –

संबंधित मुद्दे