How can I solve this C# polymorphism problem? - c#

I have a base class, SpecialClass. Lots of other classes inherit from it, like WriteSpecialClass and ReadSpecialClass. Instances of these classes are serialized and deserialized, after being casted to/from the base class. Therefore, I have lots of repetitious code like this (for serializing):
SpecialClass sc = null;
if (type.DisplayName == "Read") {
sc = new ReadSpecialClass();
} else if (type.DisplayName == "Write") {
sc = new WriteSpecialClass();
} else
throw new NotSupportedException("Type not supported");
String xml = SerializeToXML(sc);
.. and deserializing:
SpecialClass sc = null;
sc = (SpecialClass)DeserializeFromXML(xml);
switch (someVariableThatDicatesTypeOfSpecialClass) {
case "Read":
sc = (ReadSpecialClass)sc;
break;
case "Write":
sc = (WriteSpecialClass)sc;
break;
}
One more important piece of info: SpecialClass has a bunch of abstract methods defined which each class that inherits from it needs to implement. All the classes which inherit from it conform to the methods, but return different things, so each class is not the same.
I can post my serialization or deserialization methods if needed.
I would like to try and simplify this so that I don't have to specify each SpecialClass-derived class (like ReadSpecialClass). Is there any way to do this? The only way I can think of is duck-typing. Thanks for your help,

Have you considered a serialize() method in SpecialClass? That way if there are some special considerations you can override the base.serialize method and take care of any unique needs. In this way it already knows what it is and the conditional isn't necessary.
Another thing to consider is maybe a helper class with a facade pattern. Then you can have a a method called "public SpecialClass DeserializeSpecialClass()". Then instead of casting the type in the deserializer you could cast it at the target. If you find you are doing too much casting then maybe consider adding abstract methods to the base class that will be realized in the derived class.
Hope this helps.

For serializing, you can use some sort of lookup:
public class SpecialClass
{
private static Dictionary<string, Func<SpecialClass>> factories =
new Dictionary<string, Func<SpecialClass>>();
static SpecialClass()
{
factories["Read"] = () => new ReadSpecialClass();
factories["Write"] = () => new WriteSpecialClass();
}
public static SpecialClass CreateByName(string name)
{
Func<SpecialClass> factory;
if (!factories.TryGetValue(name))
throw new ArgumentException("name", "\"" name +
"\" is not a recognized subclass of SpecialClass.");
return factory();
}
}
For deserialization, these two lines:
sc = (ReadSpecialClass)sc;
sc = (WriteSpecialClass)sc;
perform no actual conversion. The only thing they will do is throw an exception if the object referenced by sc is not of the appropriate type. What you are doing here is roughly the same thing as:
object a = "foo";
a = (string)a;
Sure, the cast will succeed. But it does not in any way modify the object pointed to by a. All it really does is verify that this object is already a string.

if (sc is WriteSpecialClass)
{
sc = (WriteSpecialClass) sc;
}
else if (sc is ReadSpecialClass)
{
sc = (ReadSpecialClass) sc;
}
else
{
throw new NotSupportetException("Type not Supportet");
}

If you're not concerned about performance, you can use reflection to initialize the types whose name contains that type name.
SerializeToXML() takes the base class SpecialClass as a parameter, so it shouldn't distinguish the difference between the derived classes ReadSpecialClass and WriteSpecialClass. Why don't you have SerializeToXML() as an instance method of the base class?

Rule 1./ Your derived classes should be responsible for serializing and deserializing themselves, as they should be the only ones that have intimate knowlege of the additional data that is stored in the XML Doc.
So from a ploymorphic point of view (note your code above is not polymorphic) you would do something like
public class SpecialClass
{
public virtual XElement SerializeToXML()
{
// Base impl
}
}
public class YourDerivedClasses
{
public override XElement SerializeToXML()
{
//derived implementation
}
}
Pretty straight forward. But the problem you have is not one of serialization or polymorphic behaviour, it's one of instantiation of the correct type from the XML.
One way to solve that problem is to have some key in your XML doc that specifies the type that saved it, and register a factory responsible for construction of the derived type indexed by that key (attributes are good for that kind of registration), once the derived type is constructed use it to deserialize the xml.
Note, your factory could also be a static method on the Base class.
Something like, (untested of course)....
public class SpecialClass
{
***snip
public static IEnumerable<SpecialClass> GetAllClasses(XElement xml)
{
IDictionary keyedtypes = GetKeyedTypesDictUsingReflection() // the registered factories
foreach(var x in xml.Elements("YourClassesNode"))
{
string key = //get key from xml
yield return keyedTypes[key].DeserializeFromXML(x);
}
}
}

Related

Serialize nested Interface properties without any boilerplate code [duplicate]

I would like to XML serialize an object that has (among other) a property of type IModelObject (which is an interface).
public class Example
{
public IModelObject Model { get; set; }
}
When I try to serialize an object of this class, I receive the following error:
"Cannot serialize member Example.Model of type Example because it is an interface."
I understand that the problem is that an interface cannot be serialized. However, the concrete Model object type is unknown until runtime.
Replacing the IModelObject interface with an abstract or concrete type and use inheritance with XMLInclude is possible, but seems like an ugly workaround.
Any suggestions?
This is simply an inherent limitation of declarative serialization where type information is not embedded within the output.
On trying to convert <Flibble Foo="10" /> back into
public class Flibble { public object Foo { get; set; } }
How does the serializer know whether it should be an int, a string, a double (or something else)...
To make this work you have several options but if you truly don't know till runtime the easiest way to do this is likely to be using the XmlAttributeOverrides.
Sadly this will only work with base classes, not interfaces. The best you can do there is to ignore the property which isn't sufficient for your needs.
If you really must stay with interfaces you have three real options:
Hide it and deal with it in another property
Ugly, unpleasant boiler plate and much repetition but most consumers of the class will not have to deal with the problem:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { /* code here to convert any type in Foo to string */ }
set { /* code to parse out serialized value and make Foo an instance of the proper type*/ }
}
This is likely to become a maintenance nightmare...
Implement IXmlSerializable
Similar to the first option in that you take full control of things but
Pros
You don't have nasty 'fake' properties hanging around.
you can interact directly with the xml structure adding flexibility/versioning
Cons
you may end up having to re-implement the wheel for all the other properties on the class
Issues of duplication of effort are similar to the first.
Modify your property to use a wrapping type
public sealed class XmlAnything<T> : IXmlSerializable
{
public XmlAnything() {}
public XmlAnything(T t) { this.Value = t;}
public T Value {get; set;}
public void WriteXml (XmlWriter writer)
{
if (Value == null)
{
writer.WriteAttributeString("type", "null");
return;
}
Type type = this.Value.GetType();
XmlSerializer serializer = new XmlSerializer(type);
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
serializer.Serialize(writer, this.Value);
}
public void ReadXml(XmlReader reader)
{
if(!reader.HasAttributes)
throw new FormatException("expected a type attribute!");
string type = reader.GetAttribute("type");
reader.Read(); // consume the value
if (type == "null")
return;// leave T at default value
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
reader.ReadEndElement();
}
public XmlSchema GetSchema() { return(null); }
}
Using this would involve something like (in project P):
public namespace P
{
public interface IFoo {}
public class RealFoo : IFoo { public int X; }
public class OtherFoo : IFoo { public double X; }
public class Flibble
{
public XmlAnything<IFoo> Foo;
}
public static void Main(string[] args)
{
var x = new Flibble();
x.Foo = new XmlAnything<IFoo>(new RealFoo());
var s = new XmlSerializer(typeof(Flibble));
var sw = new StringWriter();
s.Serialize(sw, x);
Console.WriteLine(sw);
}
}
which gives you:
<?xml version="1.0" encoding="utf-16"?>
<MainClass
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<RealFoo>
<X>0</X>
</RealFoo>
</Foo>
</MainClass>
This is obviously more cumbersome for users of the class though avoids much boiler plate.
A happy medium may be merging the XmlAnything idea into the 'backing' property of the first technique. In this way most of the grunt work is done for you but consumers of the class suffer no impact beyond confusion with introspection.
The solution to this is using reflection with the DataContractSerializer. You don't even have to mark your class with [DataContract] or [DataMember]. It will serialize any object, regardless of whether it has interface type properties (including dictionaries) into xml. Here is a simple extension method that will serialize any object into XML even if it has interfaces (note you could tweak this to run recursively as well).
public static XElement ToXML(this object o)
{
Type t = o.GetType();
Type[] extraTypes = t.GetProperties()
.Where(p => p.PropertyType.IsInterface)
.Select(p => p.GetValue(o, null).GetType())
.ToArray();
DataContractSerializer serializer = new DataContractSerializer(t, extraTypes);
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
serializer.WriteObject(xw, o);
return XElement.Parse(sw.ToString());
}
what the LINQ expression does is it enumerates each property,
returns each property that is an interface,
gets the value of that property (the underlying object),
gets the type of that concrete object
puts it into an array, and adds that to the serializer's list of known types.
Now the serializer knows how about the types it is serializing so it can do its job.
If you know your interface implementors up-front there's a fairly simple hack you can use to get your interface type to serialize without writing any parsing code:
public interface IInterface {}
public class KnownImplementor01 : IInterface {}
public class KnownImplementor02 : IInterface {}
public class KnownImplementor03 : IInterface {}
public class ToSerialize {
[XmlIgnore]
public IInterface InterfaceProperty { get; set; }
[XmlArray("interface")]
[XmlArrayItem("ofTypeKnownImplementor01", typeof(KnownImplementor01))]
[XmlArrayItem("ofTypeKnownImplementor02", typeof(KnownImplementor02))]
[XmlArrayItem("ofTypeKnownImplementor03", typeof(KnownImplementor03))]
public object[] InterfacePropertySerialization {
get { return new[] { InterfaceProperty }; ; }
set { InterfaceProperty = (IInterface)value.Single(); }
}
}
The resulting xml should look something along the lines of
<interface><ofTypeKnownImplementor01><!-- etc... -->
You can use ExtendedXmlSerializer. This serializer support serialization of interface property without any tricks.
var serializer = new ConfigurationContainer().UseOptimizedNamespaces().Create();
var obj = new Example
{
Model = new Model { Name = "name" }
};
var xml = serializer.Serialize(obj);
Your xml will look like:
<?xml version="1.0" encoding="utf-8"?>
<Example xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples.Simple;assembly=ExtendedXmlSerializer.Samples">
<Model exs:type="Model">
<Name>name</Name>
</Model>
</Example>
ExtendedXmlSerializer support .net 4.5 and .net Core.
Replacing the IModelObject interface with an abstract or concrete type and use inheritance with XMLInclude is possible, but seems like an ugly workaround.
If it is possible to use an abstract base I would recommend that route. It will still be cleaner than using hand-rolled serialization. The only trouble I see with the abstract base is that your still going to need the concrete type? At least that is how I've used it in the past, something like:
public abstract class IHaveSomething
{
public abstract string Something { get; set; }
}
public class MySomething : IHaveSomething
{
string _sometext;
public override string Something
{ get { return _sometext; } set { _sometext = value; } }
}
[XmlRoot("abc")]
public class seriaized
{
[XmlElement("item", typeof(MySomething))]
public IHaveSomething data;
}
Unfortunately there's no simple answer, as the serializer doesn't know what to serialize for an interface. I found a more complete explaination on how to workaround this on MSDN
Unfortuantely for me, I had a case where the class to be serialized had properties that had interfaces as properties as well, so I needed to recursively process each property. Also, some of the interface properties were marked as [XmlIgnore], so I wanted to skip over those. I took ideas that I found on this thread and added some things to it to make it recursive. Only the deserialization code is shown here:
void main()
{
var serializer = GetDataContractSerializer<MyObjectWithCascadingInterfaces>();
using (FileStream stream = new FileStream(xmlPath, FileMode.Open))
{
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
var obj = (MyObjectWithCascadingInterfaces)serializer.ReadObject(reader);
// your code here
}
}
DataContractSerializer GetDataContractSerializer<T>() where T : new()
{
Type[] types = GetTypesForInterfaces<T>();
// Filter out duplicates
Type[] result = types.ToList().Distinct().ToList().ToArray();
var obj = new T();
return new DataContractSerializer(obj.GetType(), types);
}
Type[] GetTypesForInterfaces<T>() where T : new()
{
return GetTypesForInterfaces(typeof(T));
}
Type[] GetTypesForInterfaces(Type T)
{
Type[] result = new Type[0];
var obj = Activator.CreateInstance(T);
// get the type for all interface properties that are not marked as "XmlIgnore"
Type[] types = T.GetProperties()
.Where(p => p.PropertyType.IsInterface &&
!p.GetCustomAttributes(typeof(System.Xml.Serialization.XmlIgnoreAttribute), false).Any())
.Select(p => p.GetValue(obj, null).GetType())
.ToArray();
result = result.ToList().Concat(types.ToList()).ToArray();
// do the same for each of the types identified
foreach (Type t in types)
{
Type[] embeddedTypes = GetTypesForInterfaces(t);
result = result.ToList().Concat(embeddedTypes.ToList()).ToArray();
}
return result;
}
I have found a simpler solution (you don't need the DataContractSerializer), thanks to this blog here:
XML serializing derived types when base type is in another namespace or DLL
But 2 problems can rise in this implementation:
(1) What if DerivedBase is not in the namespace of class Base, or even worse in a project that depends on Base namespace, so Base cannot XMLInclude DerivedBase
(2) What if we only have class Base as a dll ,so again Base cannot XMLInclude DerivedBase
Till now, ...
So the solution to the 2 problems is by using XmlSerializer Constructor (Type, array[]) :
XmlSerializer ser = new XmlSerializer(typeof(A), new Type[]{ typeof(DerivedBase)});
A detailed example is provided here on MSDN:
XmlSerializer Constructor (Type, extraTypesArray[])
It seems to me that for DataContracts or Soap XMLs, you need to check the XmlRoot as mentioned here in this SO question.
A similar answer is here on SO but it isn't marked as one, as it not the OP seems to have considered it already.
in my project, I have a
List<IFormatStyle> FormatStyleTemplates;
containing different Types.
I then use the solution 'XmlAnything' from above, to serialize this list of different types.
The generated xml is beautiful.
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[XmlArray("FormatStyleTemplates")]
[XmlArrayItem("FormatStyle")]
public XmlAnything<IFormatStyle>[] FormatStyleTemplatesXML
{
get
{
return FormatStyleTemplates.Select(t => new XmlAnything<IFormatStyle>(t)).ToArray();
}
set
{
// read the values back into some new object or whatever
m_FormatStyleTemplates = new FormatStyleProvider(null, true);
value.ForEach(t => m_FormatStyleTemplates.Add(t.Value));
}
}

Choose interface child by some property

I have an interface:
public interface IPath
{
// Some method
}
and I have two classes which are inheriting this interface
public class First : IPath { }
public class Second: IPath { }
By the way, in some method I need to choose which class to use, First or Second, it depending on one string property (type), which I get from database. It looks like:
public void SomeMethod(string type)
{
if (type == "First") { // creating instance of First class }
else if (type == "Second") { // creating instance of Second class }
else { ... }
}
Question is: how can I avoid if/else or switch/case constructions and automaticly create the right instance, depending on the string variable?
You could create a dictionary to map from string to Type and the use that Type and Activator.CreateInstance to create an instance of that type.
Alternatively you could fetch the type using reflection and not need a dictionary at all
private Dictionary<string, Type> _iPathMapping = new Dictionary<string, Type>
{
{ nameof(First), typeof(First) },
{ nameof(Second), typeof(Second) },
};
// ...
public IPath Create(string path)
{
var type = _iPathMapping[path];
return (IPath) Activator.CreateInstance(type);
}
(You'd want to extend that code with safety checks, see below)
But this is fundamentally a bad solve. The problem to this is that it's harder to pass parameters to constructors and it's unsafe as well, if any of your implementations don't have a parameterless constructor, this will fail, but not with a compiler error, no it will fail during runtime, i.e once a user (or hopefully testers/ automatic tests) ran into the problem. So a better way would be to store a method that's invoked to construct the type instead of using Activator.CreateInstance, something like
private Dictionary<string, Func<IPath>> _iPathMapping = new Dictionary<string, Func<IPath>>
{
{ nameof(First), () => new First() },
{ nameof(Second), () => new Second() },
};
// ...
public IPath Create(string path)
{
if (_iPathMapping.TryGetValue(path, out var func))
return func.Invoke();
return null;
}
This solves the problem of parameters for the constructor in that it doesn't throw a runtime exception.
But we still haven't solved the problem of actually passing parameters to the constructor, especially when First and Second require different parameters. The only clean* way to I can think of to handle this in a generic and reusable way is using a Dependency Injection framework/ context to actually construct our instances.
But in general, the if/ else if chain or switch statement isn't necessarily a bad thing, you even see it in some places inside .NET
* You could replicate the part of a DI framework that's responsible for resolving dependencies for a constructor, but that's just re-implementing the wheel and might as well save the effort needed and just pull in a dependency like Microsoft.Extensions.DependencyInjection
I have a shorter version as answer, but I saw "MindSwipe" already offered you one:
Dictionary<string, Type> map = new Dictionary<string, Type>();
map.Add("First", typeof(First));
map.Add("Second", typeof(Second));
var instance = Activator.CreateInstance(map[<your parameter as string>]);

How can I store a returned List<T> in c#?

This should be simple, but I keep running into problems with this. I have a generic class Csvs<T>with a method that returns a type List<T> through a Property called Dockets. (The method reads CSV files and returns the list of the records in that CSV file. T is the type of that CSV file, which is defined in a series of models, which contain the header row as Properties)
In my calling class (which I cannot make generic), I have a switch statement based on an enum that contains the names of the classes I use in T.
switch (_userInput.CsvType)
{
case CsvType.Type1:
var csvType1 = new Csvs<Type1>(_userInput);
_dockets = csvType1.Dockets;
break;
case CsvType.Type2:
var csvType2 = new Csvs<Type2>(_userInput);
_dockets = csvType2.Dockets;
break;
case CsvType.Type3:
var csvType3 = new Csvs<Type3>(_userInput);
_dockets = csvType3.Dockets;
break;
// more cases here [...]
default:
throw new ArgumentOutOfRangeException();
}
(It would be nice if I could find a better way than using this switch, too, but it works for now.)
What does not work, or what I do not know how to do is, how to declare _dockets? Since T could be any of the types. I've tried extracting an interface ICsvs and have the class inherit it, and then declare
var _dockets = new List<ICsvs>;
But this throws an error, that I can't implicitly convert the List<Type1> to a List<ICsvs>.
And I can't cast the List to a type List<ICsvs>. (Then I get the next exception)
Cannot convert type 'System.Collections.Generic.List' to 'System.Collections.Generic.List' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion
The same happens if I use an abstract class.
So, how do I store the returned List in the calling class? How do I declare a field or property that can hold any of the types?
The simplest way to achieve this is to make all of your types implementing ICsv, like this:
interface ICsv { }
class CsvType1 : ICsv { }
class CsvType2 : ICsv { }
class Csvs<TCsvModel>
where TCsvModel: ICsv
{
public Csvs(IList<TCsvModel> csvModel)
{
this.Dockets = csvModel;
}
public IList<TCsvModel> Dockets { get; private set;}
}
So you can use this like:
IEnumerable<ICvs> dockets;
var cvsType1 = new Csvs<CsvType1>(_input);
dockets = cvsType1.Dockets
var cvsType2 = new Csvs<CsvType2>(_input);
dockets = cvsType2.Dockets
The most meaningful/polymorphic thing you can do is make the Dockets property return an interface describing what you do with them. Depending on your requirements, that may or may not be feasible - but you have little other option. As I commented, Generics really only works well when you know the type at compile-time.
By way of an example. An interface
public interface IDockets
{
void DoSomethingWithRecords();
}
A generic implementation which will be returned from your Csvs class
public class Dockets<T> : IDockets
{
private IEnumerable<T> dockets;
public Dockets(IEnumerable<T> dockets)
{
this.dockets = dockets;
}
public void DoSomethingWithRecords()
{
foreach(var docket in dockets)
Console.WriteLine(docket); // do whatever
}
}
This class would then be returned from your Csvs class Dockets property
public class Csvs<T>
{
private List<T> parsedDockets; // assume you have something like this:
public IDockets Dockets{ get { return new Dockets(parsedDockets); } }
}
And your calling code
IDockets dockets = null;
switch (_userInput.CsvType)
{
case CsvType.Type1:
var csvType1 = new Csvs<Type1>(_userInput);
dockets = csvType1.Dockets;
break;
// Snip //
}
dockets.DoSomethingWithRecords();
I think for what I was after, that is, storing the return value for later use, dynamic is the best answer (Thank you #Rahul). I can worry about the type later. Thank you all for your help.
dynamic _dockets;
then switch-case through the returns.

Using generic method in an interface to return a List of variable type based on input from a file

I have an application that needs to read data records from files and create lists of objects based on the type of record it is reading. There are six possible types of records that could be read in. I discovered the FileHelpers library to facilitate creating objects of whatever type I specify. I'm trying to write this in such a way that one reader/file parser class can handle returning any type of record read in. I am exploring the possibility of using a generic interface/method to do this, but am kind of stuck on how to correctly implement it.
I have a ReaderFactory that will give me a specific type of Reader back, and all Readers inherit from MainReader.
public interface MainReader<T>
{
void ReadFile(string vsFileName);
List<T> GetRecords();
}
public class ReaderFactory<T>
{
MainReader<T> GetReader(RecordType recordType)
{
MainReader<T> oReader = null;
switch (recordType)
{
case RecordType.TypeA:
oReader = new TypeAReader();
break;
case RecordType.TypeB:
oReader = new TypeBReader();
break;
}
return oReader;
}
The readers themselves will take a file and, using the FileHelpers library, read the data into a list of objects.
class TypeAReader : MainReader<RecordTypeA>
{
public List<RecordTypeA> oRecordList = new List<RecordTypeA>();
public void ReadFile(string fileName)
{
FileHelperEngine<RecordTypeA> oEngine = new FileHelperEngine<RecordTypeA>();
RecordTypeA[] records = oEngine.ReadFile(fileName);
foreach(RecordTypeA record in records)
{
oRecordList.Add(record);
}
}
public List<RecordTypeA> GetRecords()
{
return oRecordList;
}
}
When building, I get the error "cannot implicitly convert type TypeAReader to MainReader". If I change the factory to return a new TypeAReader or TypeAReader, I get the same error that I can't convert TypeAReader to MainReader.
I believe there is a way to do this, I must just be missing something. Any help is very much appreciated.
For you compilation issue - you missing a cast: oReader =(MainReader<T>)new TypeAReader() will do a job. If you want to understand why it doesn't get compiled consider next call:
new ReaderFactory<int>().GetReader(RecodrTypeA)
Obviously TypeAReader doesn't implement MainReader<int> interface.
Regarding a design of your factory, compiler actually hints you that where is no relation between RecordType enum and T. So first of all you can omit RecordType descriptor:
public class ReaderFactory
{
MainReader<T> GetReader<T>()
{
var returnType = typeof(T);
if(returnType==typeof(RecordTypeB))
return (MainReader<T>)new TypeBReader();
if (returnType == typeof(RecordTypeA))
return (MainReader<T>)new TypeAReader();
throw new NotImplementedException();
}
}
you can also add some static type protection your factory by limiting T to an interface to prevent GetReader<int>() calls (still arguable)
interface IRecordType{}
class RecordTypeA:IRecordType{}
class RecordTypeB:IRecordType{}
...
MainReader<T> GetReader<T>() where T:IRecordType

In Protobuf-net how can I pass an array of type object with objects of different types inside, knowing the set of potential types in advance

I am trying to migrate existing code that uses XmlSerializer to protobuf-net due to the increased performance it offers, however I am having problems with this specific case.
I have an object[] that includes parameters that are going to be sent to a remote host (sort of a custom mini rpc facility). I know the set of types from which these parameters can be, but I cannot tell in advance in which order they are going to be sent.
I have three constraints. The first is that I am running in Compact Framework, so I need something that works there. Second, as I mentioned performance is a big concern (on the serializing side) so I would rather avoid using a lot of reflection there if possible. And the most important is that I care about the order in which this parameters were sent.
Using XmlSerializer it was easy just adding XmlInclude, but for fields there is nothing equivalent as far as I know in Protobuf-net. So, is there a way to do this? Here is a simplified example.
[Serializable]
[XmlInclude(typeof(MyType1)),
XmlInclude(typeof(MyType2)),
XmlInclude(typeof(MyType3))
public class Message()
{
public object[] parameters;
public Message(object[] parms)
{
parameters = parms;
}
}
Message m = new Message(new object[] {MyType1(), 33, "test",
new MyType3(), new MyType3()});
MemoryStream ms = new MemoryStream();
XmlSerializer xml = new XmlSerializer(typeof(Message));
xml.Serialize(ms,xml);
That will just work with XmlSerializer, but if I try to convert it to protobuf-net I will get a "No default encoding for Object" message.
The best I came up with is to use generics and [ProtoInclude] as seen in this example. Since I can have different object types within the array this doesn't quite make it. I added a generic List for each potential type and a property with [ProtoIgnore] with type object[] to add them and get them. I have to use reflection when adding them (to know in which array to put each item) which is not desirable and I still can't preserve the ordering as I just extract all the items on each list one by one and put them into a new object[] array on the property get.
I wonder if there is a way to accomplish this?
I tried what Marc suggested below, but I couldn't get it to work. I think I may have misunderstood something.
Using the code you wrote. I thought I should use MessageParam Create to generate MessageParam objects to add to the list. So basically I added a constructor to Message like this:
public Message(object[] parms)
{
foreach (object o in parms)
{
parameters.Add(MessageParam.Create(o));
}
}
But, if i do that I will get "Unexpected type found during serialization; types must be included with ProtoIncludeAttribute; found MessageParam`1 passed as MessageParam" because I assume the serializer is expecting the non-generic version. Did I misunderstand your suggestion? If so, what is the right thing to do?
object is going to be problematic. I would try something more like:
[ProtoContract]
class Message
{
private readonly List<MessageParam> parameters = new List<MessageParam>();
[ProtoMember(1)]
public List<MessageParam> Parameters { get { return parameters; } }
}
[ProtoContract]
[ProtoInclude(3, typeof(MessageParam<int>))]
[ProtoInclude(4, typeof(MessageParam<float>))]
[ProtoInclude(5, typeof(MessageParam<DateTime>))]
//...known types...
abstract class MessageParam {
public abstract object UntypedValue { get; set; }
public static MessageParam<T> Create<T>(T value) {
return new MessageParam<T> { Value = value };
}
public static MessageParam CreateDynamic(object value)
{
Type type = value.GetType();
switch (Type.GetTypeCode(value.GetType()))
{
// special cases
case TypeCode.Int32: return Create((int)value);
case TypeCode.Single: return Create((float)value);
case TypeCode.DateTime: return Create((DateTime)value);
// fallback in case we forget to add one, or it isn't a TypeCode
default:
MessageParam param = (MessageParam)Activator.CreateInstance(
typeof(MessageParam<>).MakeGenericType(type));
param.UntypedValue = value;
return param;
}
}
}
[ProtoContract]
sealed class MessageParam<T> : MessageParam
{
[ProtoMember(1)]
public T Value { get; set; }
public override object UntypedValue
{
get { return Value; }
set { Value = (T)value; }
}
}
Note that the unreleased "v2" code offers much more ability to define the relationships at runtime rather than through attributes (which is quite limiting here).

Categories

Resources