2

Suppose a simple class in C#:

[Serializable]
public class MyClass
{
    public int A { get; set; }
    public int B { get; set; }
    [XmlIgnore]
    public int Sum { get; }
}

This is deserialized from a simple XML file containing values for A and B. However, Sum is calculated with A and B, not serialized. Assume that I don't want to calculate Sum on the fly in the accessor. How can I pre-calculate Sum? The constructor is called first, naturally, meaning A and B not asigned later, and thus are no use yet to calculate Sum. Is there some kind of post-deserialization or post-instanciation thingie I could use so that the object is completely created in one step? I just don't want my objects to ever be in an incomplete and invalid state.

MPelletier
  • 16,256
  • 15
  • 86
  • 137

4 Answers4

5

I think you are trying to ask something else. This use case does not justify the complexity of solution you are asking for.

If this is the use case:

  1. Calculate on the fly. Turn Sum into a method.
  2. Use backing fields if you are concerned about overhead.

e.g.

if (_alreadyCalculated) 
{
    return _sum;
}
_sum = A+B;
_alreadyCalculated = true;
return _sum;
MPelletier
  • 16,256
  • 15
  • 86
  • 137
Rekha G
  • 76
  • 1
  • Well, yes, I am trying to ask something else but I wanted to simplify it to not get lost in the details. The real context if you must know is of two time stamps, broken down into each constituent part (year, month, date, hour, minute, seconds), but only one of the two has a date, the other is just a time. And there's a check to make, if time A is greater than time B, then time B in on the next day. – MPelletier Sep 13 '11 at 00:12
  • While still somewhat small, I still want a single operation. Thanks for your input though, I appreciate it. – MPelletier Sep 13 '11 at 00:14
  • 1
    @MPelletier I feel that this is the obvious, simplest solution - use lazy evaluation on the accessor; check whether _sum is 0 (or use a nullable field and check whether null), evaluate if necessary, and return. – Kirk Broadhurst Sep 13 '11 at 00:57
  • @Kirk Broadhurst or if .Net4 is available use the Lazy object to do this logic for you. – Bob Vale Sep 13 '11 at 12:15
3

I think you are looking for the OnDeserializedAttribute that marks a method as needing to be called after deserializing the object

EDIT -- missed the XmlSerializer part....

Your best bet would be to recalculate sum in the setter methods of A and B

Alternatively you could make the Sum getter use Lazy

dbc
  • 104,963
  • 20
  • 228
  • 340
Bob Vale
  • 18,094
  • 1
  • 42
  • 49
2

You should use regular properties and recalculate Sum in the setters for A and B.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
2

One way you could do it is to modify the serializer yourself. You can use SGen to get the code for the serializer as follows:

sgen /f /k /a:MyAssembly.dll

This will leave the XmlSerializer 'temp' file in the current directory. Add that code to your project and change it.

global::ConsoleApplication31.Foo Read2_Foo(bool isNullable, bool checkType) {
    // ...
    if (Reader.IsEmptyElement) {
        Reader.Skip();
        return Callback(o);
    }
    // ...
    return Callback(o);
}

private T Callback<T>(T value) {
    if (value is IDeserializationCallback)
        ((IDeserializationCallback)value).OnDeserialization(this);
    return value;
}

You can then instantiate the serializer directly in your code. You could go as far as to automate this with something like NRefactory (look for Read_* methods and change any return lines) - if you don't this you will have maintenance overhead. Sample usage:

var ser = new FooSerializer();
using (var sr = new StringReader("<foo />"))
{
    var foo = ser.Deserialize(sr);
}
Console.ReadLine();
Jonathan Dickinson
  • 9,050
  • 1
  • 37
  • 60