2012-02-23 22 views
23

Mam klasę, która definiuje chronione pole. Chronione pole ma inicjator pola.Inserator pola w klasie C# nie jest uruchamiany podczas odwzorowywania

Po deserializacji konkretnej klasy inicjalizator pola nie jest uruchamiany. Czemu? Jaki jest najlepszy wzór, aby rozwiązać problem? Jeśli przeniesię inicjalizację do konstruktora, konstruktor również nie zostanie wywołany.

[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 
    } 
} 

ORIGINAL hierarchii klas

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

WSPOMAGAJĄCE METODY

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

Próbowałem dodać chroniony konstruktor w abstrakcyjnym miejscu, w którym zainicjowałeś dyktando? Podaj publiczny konstruktor w MyConcrete, który prowadzi łańcuch do ': base()'. – Will

+0

Spłaszczyłem to w jednej klasie i dodałem konstruktora. Konstruktor nie jest wywoływany. Znalazłem ten powiązany wpis, że inicjatory pól i konstruktory pól nie są wywoływane ... http://stackoverflow.com/questions/5021973/constructors-not-called-on-deserialization –

Odpowiedz

39

Na deserializacji neither the constructors nor the field initializers are called i "pustym" un-zainicjowany obiekt jest używany zamiast.

Aby rozwiązać go można skorzystać z następujących OnDeserializing lub OnDerserialized atrybuty mają Deserializator wywołać funkcję z następującym podpisem:

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

z tą funkcją jest, gdzie można zainicjować cokolwiek została pominięta w deserializacji proces.

Jeśli chodzi o konwencję, to zazwyczaj mój konstruktor wywołuje metodę OnCreated(), a następnie również wywołuje metodę deserializacji, nazywając to samo. Następnie możesz obsłużyć wszystkie inicjalizacje pola i upewnić się, że są uruchamiane przed deserializacją.

[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

Nie mogę przypisać pola readonly z metody OnDeserializing() ... ten wybór projektu dla serializera łamie możliwość korzystania z readonly ... Nadal wydaje się być najlepszym/jedynym podejściem. –

+0

O tak ... Masz rację co do tego, co czytasz. Woops, tęskniłem za tym! – Reddog

+0

@Reddog +1 Dzięki za to rozwiązanie, mogę żyć bez readonly – surfen

8

Innym rozwiązaniem jest dostęp do pola przez chronioną (w przykładzie) nieruchomości, a zainicjować pole za pomocą operatora

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

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

Wady są takie, że można stracić korzyści readonlynull-coalescing (??), i upewnij się, że masz dostęp tylko do wartości za pośrednictwem właściwości.

+0

+1, zapewnia semantykę podobną do * readonly *, o ile wszyscy programiści pracujący na tej klasie rozumieją konwencję dostępu do 'MyDict' zamiast' myDict'. Gdzieś, co spowoduje błąd, ale opcja odpowiednia do pewnych okoliczności. –

+1

To przyniesie ci lenistwo dla twojej klasy - dla właściwości tylko do odczytu jest fajnie. – EvgeniyK

+0

Dlaczego nie usunąć inicjatora pola w ogóle?Nie ma powodu, aby tworzyć słownik, dopóki go nie potrzebujesz. –

Powiązane problemy