jamescourtney / FlatSharp

Fast, idiomatic C# implementation of Flatbuffers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

OnInitialized invoked too early with non-virtualized members

RyouBakura opened this issue · comments

Hello,

it seems that OnInitialized is getting invoked before things are deserialized with a greedy table like this:

table Character (fs_serializer, fs_nonVirtual) 
{
    // ...
}

Generated subclass constructor:

public tableReader_261a33b842a84271a44efba629b74851(TInputBuffer buffer, int offset) 
: base(__CtorContext)
{
    checked
    {
        buffer.InitializeVTable(offset, out var __vtableLocation, out var __vtableMaxIndex);
        base.Shortname = ReadIndex0Value(buffer, offset, __vtableLocation, __vtableMaxIndex);
        //...
    }
}

Generated base class constructor:

protected Character(FlatBufferDeserializationContext context)
{
    checked
    {
        this.OnInitialized(context);
    }
}

Since this is a protected base constructor, it should be safe to move the OnInitialized(context) call into the subclass constructor after everything is deserialized?

This is interesting. What we'd probably need to do is something along the lines of:

Base class:

protected Character(FlatBufferDeserializationContext context)
{
    checked
    {
        this.InvokeOnInitialized(true, context);
    }
}

protected void InvokeOnInitialized(bool fromBaseClass, FlatBufferDeserializationContext context)
{
     bool isBase = this.GetType() == typeof(Character);
     if (isBase == fromBaseClass)
     {
           this.OnInitialized(context);
     }
}

Derived class:

public tableReader_261a33b842a84271a44efba629b74851(TInputBuffer buffer, int offset) 
: base(__CtorContext)
{
    checked
    {
        buffer.InitializeVTable(offset, out var __vtableLocation, out var __vtableMaxIndex);
        base.Shortname = ReadIndex0Value(buffer, offset, __vtableLocation, __vtableMaxIndex);
        //...

        base.InvokeOnInitialized(false, __CtorContext);
    }
}

This example might be a bit contrived since it's using the protected ctor here (so we could elide the check), but would probably be necessary for the public constructors. I don't think this change would be breaking, but I need to sleep on it to see if it feels right to me or not. I'm having some sort of reaction to the type equality test.

Do you have any suggestions?

My idea was this:

Baseclass:

public Character()
{
    checked
    {
        this.OnInitialized(null); // <-- keep this
    }
}
protected Character(FlatBufferDeserializationContext context)
{
    checked
    {
        // this.OnInitialized(context);
        // <---- don't do anything here
    }
}

Subclass:

public tableReader_261a33b842a84271a44efba629b74851(TInputBuffer buffer, int offset) 
: base(__CtorContext)
{
    checked
    {
        buffer.InitializeVTable(offset, out var __vtableLocation, out var __vtableMaxIndex);
        base.Shortname = ReadIndex0Value(buffer, offset, __vtableLocation, __vtableMaxIndex);
        //...

        base.OnInitialized(__CtorContext); // <---- and move it here?
    }
}

Your way is better :)

I shouldn't design things before caffeine! I'll take a look at this soon. Thanks for reaching out.

#139

The actual implementation ended up being a little trickier, since the partial method is private. So now there is a OnDeserialized method that can be optionally defined by anyone (even those using runtime code gen) and Flatsharp will invoke it at the end of constructing the deserialized object. For precompiled serializers, this is just a passthrough to OnInitialized.

I have a beta available here: https://ci.appveyor.com/project/jamescourtney/flatsharp/builds/38623593/artifacts

If you could play with it and let me know if it solves your problems, that would be appreciated. Also -- you might not care, but this includes supports for vectors of unions like we talked about in #134.

Thank you! It works perfectly and the vector of unions is super useful to have too.

Published in version 5.2