Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't serialization callback methods be virtual?

I have a deserialization method (decorated with the [OnDeserialized()] attribute) which I want to override in a derived class. When I try to do so, I get the following runtime error:

An unhandled exception of type 'System.TypeLoadException'.... Type 'BaseObj' in assembly ... has method 'OnDeserialization' which is either static, virtual, abstract or generic, but is marked as being a serialization callback method.

I couldn't find any documentation confirming this restriction on serialization callbacks (other than the error message itself). Can anyone explain this strange limitation?

Based on a suggestion in a comment here, I resorted to calling a separate virtual function from the OnDeserialization method, like so:

[Serializable()]
public class BaseObj
{   
    protected string obj { get; set; } 

    [OnDeserialized()]
    public void OnDeserializedMethod(StreamingContext context)
    {
        //call the virtual method because deserialization callbacks can’t be virtual
        onDeserialized(context);
    }    
    virtual protected void onDeserialized(StreamingContext context)
    {
        obj = "This value was deserialized by the base class.";
    }   
}
[Serializable()]
public class DerivedObj : BaseObj
{    
    override protected void onDeserialized(StreamingContext context)
    {
        obj = "This value was deserialized by the derived class.";
    }    
}

This seems to work fine, but it seems rather "kludgey". Is this really my only option? Why can't a serialization callback method be virtual?

like image 934
kmote Avatar asked Jul 19 '17 14:07

kmote


2 Answers

Since BinaryFormatter and DataContractSerializer don't call constructors when deserializing, OnDeserializedAttribute allows performing of operations you would normally do in a constructor, e.g. state initialization. Methods marked by this attribute are also executed in the same order constructors are: base first, then derived.

So for the purposes of deserialization you can treat those methods as constructors.

And virtual constructors are not allowed in C#.

like image 140
Taras Dzyoba Avatar answered Nov 13 '22 21:11

Taras Dzyoba


I am facing same issue, and I saw this question. So I did some test, and I got some interesting findings, and I think I figured out why virtual is not needed from a different angle. Here is my test.

[DataContract]
class Base
{
    [OnDeserialized]
    //cannot be virtual
    protected void OnDeserializedBase()
    {
        //base deserialization
    }
}

[DataContract]
public class Derived : Base
{
    [OnDeserialized]
    //cannot be virtual
    OnDeserializedDerived()
    {
        //Derived deserialization
    }
}

Base b = new DataContractJsonSerializer(typeof(Derived)).ReadObject(stream);

What I find is, both OnDeserializedBase and OnDeserializedDerived will be invoked, in turns. Even if there is NO OnDeserialized in the derived class, Base class's OnDeserializedBase will still be invoked.

If that is the case, it makes no benefit from making this method virtual. Because, to get the benefit of a virtual method, you need to pass a derived object into base object, and call that base.virtualMethod(). There is never a chance to do it that way. But, you don't need to worry about losing the OnDeserialized functionality even you deserialize a derived class.

So after the test, I am happly to do the way that I posted.

like image 2
Tony Avatar answered Nov 13 '22 22:11

Tony