I have a class hierarchy that looks like this. These classes contain a lot of other details which I have excluded. This is a simplification to focus on the serialization a
OK; with the updated question I understand a bit more. I expect the generics in the middle of the object-model are indeed going to make life... "fun". It doesn't work "out of the box"; I looked to see if there were some simple tweaks I could make to support it, but it started getting ugly pretty quickly. I expect it would be better to simply remove the need for the generic in the middle, if possible - perhaps retaining a generic interface (rather than the generic class). Here's some code that does work; how this maps to your code... I can't tell 100%. Note you don't have to use the TypeDescriptor stuff (etc) - it just seemed that since you are using code-gen this might make some things easier...
(I didn't check the DataSet stuff - just the class stuff)
using System;
using System.ComponentModel;
using System.Data;
using System.IO;
using NUnit.Framework;
using ProtoBuf;
[TestFixture]
public class ComplexGenericTest
{
[Test]
public void TestX()
{
Query query = new X { Result = "abc" };
Assert.AreEqual(typeof(string), query.GetQueryType());
Query clone = Serializer.DeepClone(query);
Assert.IsNotNull(clone);
Assert.AreNotSame(clone, query);
Assert.IsInstanceOfType(query.GetType(), clone);
Assert.AreEqual(((X)query).Result, ((X)clone).Result);
}
[Test]
public void TestY()
{
Query query = new Y { Result = 1234};
Assert.AreEqual(typeof(int), query.GetQueryType());
Query clone = Serializer.DeepClone(query);
Assert.IsNotNull(clone);
Assert.AreNotSame(clone, query);
Assert.IsInstanceOfType(query.GetType(), clone);
Assert.AreEqual(((Y)query).Result, ((Y)clone).Result);
}
}
public static class QueryExt {
public static Type GetQueryType(this IQuery query)
{
if (query == null) throw new ArgumentNullException("query");
foreach (Type type in query.GetType().GetInterfaces())
{
if (type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(IQuery<>))
{
return type.GetGenericArguments()[0];
}
}
throw new ArgumentException("No typed query implemented", "query");
}
}
public interface IQuery
{
string Result { get; set; }
}
public interface IQuery : IQuery
{
new T Result { get; set; }
}
[ProtoInclude(21, typeof(W))]
[ProtoInclude(22, typeof(X))]
[ProtoInclude(23, typeof(Y))]
[ProtoInclude(25, typeof(SpecialQuery))]
[ProtoContract]
abstract class Query : IQuery
{
public string Result
{
get { return ResultString; }
set { ResultString = value; }
}
protected abstract string ResultString { get; set; }
// these are to allow simple ResultString implementations
// without the codegen having to worry about int.Parse etc
protected static string FormatQueryString(T value)
{
return TypeDescriptor.GetConverter(typeof(T))
.ConvertToInvariantString(value);
}
protected static T ParseQueryString(string value)
{
return (T) TypeDescriptor.GetConverter(typeof(T))
.ConvertFromInvariantString(value);
}
}
[ProtoContract]
[ProtoInclude(21, typeof(Z))]
abstract class SpecialQuery : Query, IQuery
{
public new DataSet Result { get; set; }
[ProtoMember(1)]
protected override string ResultString
{
get {
if (Result == null) return null;
using (StringWriter sw = new StringWriter())
{
Result.WriteXml(sw, XmlWriteMode.WriteSchema);
return sw.ToString();
}
}
set {
if (value == null) { Result = null; return; }
using (StringReader sr = new StringReader(value))
{
DataSet ds = new DataSet();
ds.ReadXml(sr, XmlReadMode.ReadSchema);
}
}
}
}
[ProtoContract]
class W : Query, IQuery
{
[ProtoMember(1)]
public new bool Result { get; set; }
protected override string ResultString
{
get {return FormatQueryString(Result); }
set { Result = ParseQueryString(value); }
}
}
[ProtoContract]
class X : Query, IQuery
{
[ProtoMember(1)]
public new string Result { get; set; }
protected override string ResultString
{
get { return Result ; }
set { Result = value; }
}
}
[ProtoContract]
class Y : Query, IQuery
{
[ProtoMember(1)]
public new int Result { get; set; }
protected override string ResultString
{
get { return FormatQueryString(Result); }
set { Result = ParseQueryString(value); }
}
}
[ProtoContract]
class Z : SpecialQuery
{
}