C# generic type in a base class
-
18-09-2019 - |
Question
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();
}
}
Solution
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>
OTHER TIPS
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).
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.
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.