views:

459

answers:

4

I'm writing a system that has a set of protocol buffers (using protobuf-net), I want to define something like this in an abstract class they all inherit off:

public byte[] GetBytes()

however, the protocol buffer serealiser requires a type argument, is there some efficient way to get the type of the inheriting class?

Example:

public byte[] GetBytes()
    {
        using (MemoryStream stream = new MemoryStream())
        {
            Serializer.Serialize<T /* what goes here? */>(stream, this);
            return stream.ToArray();
        }
    }
+3  A: 

Just write "T" right?

and then in your class declaration:

public class M<T>

?

-- Edit

And then when you inherit it:

public class Foo : M<Apple>
Noon Silk
+2  A: 

Define your base class as BaseClass<T> and then your derived classes replace T with the serializer type DerivedClass<SerializerType>.

You can also specify constraints on the type argument e.g.

BaseClass<T> where T : SerializerBase

Here is a description of the types of constraints you can apply.

Simon Fox
+3  A: 

You can do this via reflection, but protobuf-net did it for you.

Just change your call to:

Serializer.NonGeneric.Serialize(stream, this /* Takes an object here */);

This works by building the generic method at runtime via reflection. For details, check the code (second method here).

Reed Copsey
How fast is this? I hear reflection is quite slow and I'm using this to serialize network packets in a game - so unless it's blazingly fast it's not good enough ;)
Martin
Reflection definitely adds some overhead (although probably less than your network packet speed, so it may not matter). Profile it to tell. You can edit your classes as mention in Silky's answer, which will give better perf, but would require you to all of your classes you want to serialize generic, just to provide the "T". That may be fine, but it's not a direct answer to your original question, since it's a change in the full class hierarchy structure. It can cause issues if you already have another base class, for example.
Reed Copsey
+1  A: 

You don't actually need anything special here... since protobuf-net respects inheritance. If you have:

[ProtoInclude(typeof(Foo), 20)]
[ProtoInclude(typeof(Bar), 21)]
public abstract class MyBase {
    /* other members */

    public byte[] GetBytes()
    {
        using(MemoryStream ms = new MemoryStream())
        {
            Serializer.Serialize<MyBase>(ms, this); // MyBase can be implicit
            return ms.ToArray();
        }
    }
}
[ProtoContract]
class Foo : MyBase { /* snip */ }
[ProtoContract]
class Bar : MyBase { /* snip */ }

then it will work. To serialize the data, it always starts at the base (contract) type; so even if you did Serializer.Serialize<Foo>(stream, obj) the first thing it will do is detect that it has a base class that is a contract, and switch to MyBase. During deserialization it will identify the correct derived (concrete) type and use that, so you can use Deserialize with MyBase too, and it will construct a Foo or Bar depending on what the original data was.

Thus the following are largely identical:

Serializer.Serialize<BaseType>(dest, obj);
...
BaseType obj = Serializer.Deserialize<BaseType>(source);

and

Serializer.Serialize<DerivedType>(dest, obj);
...
DerivedType obj = Serializer.Deserialize<DerivedType>(source);

The main difference here is how the variables are typed.

Marc Gravell
Is this faster/slower than then way I'm currently doing it? Speed is an issue.I have to say I prefer this way though!
Martin
No faster or slower. It *always* has to get back to the base and work forwards.
Marc Gravell
Oh right ok, excellent.As an aside, would you recommend using protocol buffers for serialising network packets in a game?
Martin
I know of several projects that are doing exactly this; my expertise is **not** game programming, though, so I cannot offer direct advice from experience... but *if it works*...
Marc Gravell
It does work very well, protobuf-net is excellent, thankyou very much for creating it :)
Martin
Out of interest, which other projects?
Martin