zhongliangyu / protobuf-net

Automatically exported from code.google.com/p/protobuf-net
0 stars 0 forks source link

Using an Interface on a Structure not working.. #301

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
Tristen.Fielding@gmail.com

I need to keep several very large static arrays in sync between multiple 
machines and speed is of the essence. However, the arrays are sparse arrays 
therefore, I don't need to send the entire array across the wire just the 
effected cells. Therefore, my first attempt was to using several small wrapper 
classes that when accessing a property on the class the class would index into 
one of the static arrays and return the correct value. Likewise, setting the 
property would update the correct cell in the correct array. Thus, I let 
protobuf-net read/write to the property which in turn reads from one array and 
write to the other. Once the writing is finished I discard the wrapper class. 
All, is working great... except I'm creating lots and lots of simple wrapper 
classes when deserializing the stream. Therefore, I converted all of my wrapper 
classes into structs (with a common interface) so that I was only using stack 
based memory.

The problem is that along the way my structure is lost and the serializer tries 
to serialize a new (and thus an empty) structure.

Example code:
    [ProtoContract]
    [ProtoInclude(11, typeof(MyInt))]
    internal interface ICell : INotifyPropertyChanged
    {
        CellDefinition Definition { get; }
        // More methods etc...
    }

    [ProtoContract]
    internal struct MyInt : ICell
    {
        internal MyInt(CellDefinition definition)
            : this()
        {
            Definition = definition;
        }

        private GlobalVariableDefinition _definition;

        [ProtoMember(1)]
        public GlobalVariableDefinition Definition
        {
            get { return _definition; }
            private set { _definition = value; }
        }

       // More methods etc...
    }

What steps will reproduce the problem?
1. call Serializer.PrepareSerializer<ICell>(); (I call it in the ctor of the 
WCF Service class).
2. create an instance of MyInt item = new MyInt(new Definition(...));
2. later on Serializer.SerializeWithLengthPrefix(stream, item, 
PrefixStyle.Base128) is called.
3. the serializer serializes an empty structure.
4. the deserializer deserialize the empty structure...

What is the expected output? 
I expect the structure to property serialized and deserialized.

What do you see instead?
An empty structure.

What version of the product are you using? On what operating system?
r480. Window 7 Pro

Please provide any additional information below.
I've tracked it down the issue to the CompilerContext.BuildSerializer() 
function, which calls the ctx.CastFromObject() function. The CastFromObject() 
thinks my type (i.e. the interface) is a ref type and not a value type. 

Thus, my structure is valid up until CompiledSerializer.Write(object value, 
ProtoWriter dest) is called. Then Serializer(value, dest) is called and now my 
struct is empty when the code reaches 
ProtoWriter.WriteRecursionSafeObject(object value, int key, ProtoWriter writer).

Stack trace:
>   protobuf-net.dll!ProtoBuf.ProtoWriter.WriteRecursionSafeObject(object value = 
{MyTestApp.MyInt}, int key = 4, ProtoBuf.ProtoWriter writer = 
{ProtoBuf.ProtoWriter}) Line 59 C#
    [Lightweight Function] <<<-------------- Structure is lost here!!! --------------<<<<   
    protobuf-net.dll!ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Write(object value = {MyTestApp.MyInt}, ProtoBuf.ProtoWriter dest = {ProtoBuf.ProtoWriter}) Line 45 + 0x1a bytes C#
    protobuf-net.dll!ProtoBuf.Meta.RuntimeTypeModel.Serialize(int key = 0, object value = {MyTestApp.MyInt}, ProtoBuf.ProtoWriter dest = {ProtoBuf.ProtoWriter}) Line 400 + 0x56 bytes  C#
    protobuf-net.dll!ProtoBuf.ProtoWriter.WriteObject(object value = {MyTestApp.MyInt}, int key = 0, ProtoBuf.ProtoWriter writer = {ProtoBuf.ProtoWriter}, ProtoBuf.PrefixStyle style = Base128, int fieldNumber = 0) Line 101 + 0x1d bytes C#
    protobuf-net.dll!ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(System.IO.Stream dest = {System.IO.MemoryStream}, object value = {MyTestApp.MyInt}, System.Type type = {Name = "IGlobalVariable" FullName = "MyTestApp.ICell"}, ProtoBuf.PrefixStyle style = Base128, int fieldNumber = 0, ProtoBuf.SerializationContext context = null) Line 481 + 0x14 bytes   C#
    protobuf-net.dll!ProtoBuf.Meta.TypeModel.SerializeWithLengthPrefix(System.IO.Stream dest = {System.IO.MemoryStream}, object value = {MyTestApp.MyInt}, System.Type type = {Name = "IGlobalVariable" FullName = "MyTestApp.ICell"}, ProtoBuf.PrefixStyle style = Base128, int fieldNumber = 0) Line 449 + 0x19 bytes C#
    protobuf-net.dll!ProtoBuf.Serializer.SerializeWithLengthPrefix<MyTestApp.ICell>(System.IO.Stream destination = {System.IO.MemoryStream}, MyTestApp.ICell instance = {MyTestApp.MyInt}, ProtoBuf.PrefixStyle style = Base128, int fieldNumber = 0) Line 334 + 0x61 bytes C#
    protobuf-net.dll!ProtoBuf.Serializer.SerializeWithLengthPrefix<MyTestApp.ICell>(System.IO.Stream destination = {System.IO.MemoryStream}, MyTestApp.ICell instance = {MyTestApp.MyInt}, ProtoBuf.PrefixStyle style = Base128) Line 318 + 0x35 bytes  C#

In other words, my struct get lost during the "[Lightweight function]" call.

Thanks for writing a very cool library!
-Tristen.

Original issue reported on code.google.com by Tristen....@gmail.com on 15 Jul 2012 at 4:12

GoogleCodeExporter commented 8 years ago
Thanks. There is some interface support, so I would hope this would work. I 
will investigate.

Original comment by marc.gravell on 15 Jul 2012 at 6:46

GoogleCodeExporter commented 8 years ago
This is working in the latest release! Thanks.

Original comment by arien.ha...@gmail.com on 26 Apr 2013 at 7:56