I was randomly typing 'fastest serializing c#' into Google and got protobuf.net as a result. I tried it and I think I can serialize properly yet since I can't deserilize, there is no way to tell now is there?!
When trying to deserilaize I get :
A first chance exception of type 'ProtoBuf.ProtoException' occurred in protobuf-net.dll
Cool.
The Data To Serialize:
[ProtoContract]
public struct Cow
{
[ProtoMember(1)]
public float Weight{ get; private set; }
[ProtoMember(2)]
public bool[] HadCowlings{ get; private set; }
public Cow(float weight, bool[] babies)
: this()
{
this.Weight = weight;
this.HadCowlings= (bool[])babies.Clone();
}
...
}
[ProtoContract]
public class Pasture
{
[ProtoMember(1)]
public Point Position { get; private set; }
[ProtoMember(2)]
public Cow[] Cows { get; private set; }
public static int HerdSize { get; private set; }
public static float BoundWidth { get; private set;}
public static float BoundHeight { get; private set; }
public Pasture(Cow[] Cows, Point farmPosition)
{
this.Cows = (Cow[])Cows.Clone();
Position = farmPosition;
}
...
}
[ProtoContract]
public class Farm
{
[ProtoMember(1)]
public Point FarmIDCoordinates{ get; private set; }
[ProtoMember(2)]
public List<Pasture> Pastures{ get; private set; }
public static float BoundWidth { get; private set; }
public static float BoundHeight { get; private set; }
public static int FarmSize { get; private set; }
public Farm(int x, int y, FarmType fType)
{
if (fType == RegionType.STANDARD)
Pastures = new List<Pasture>(//make a farm!);
else
Pastures = new List<Pasture>(//What he said);
FarmIDCoordinates = new Point(x, y);
}
...
}
The How:
Set:
using (ObjectSerializer serializer = new ObjectSerializer())
{
serializer.ProtoSerialize<Farm>(farm.ToString() + ".bin", aFarm)
}
Get:
using (ObjectSerializer serializer = new ObjectSerializer())
{
try
{
farmsIOwn.Add(serializer.ProtoDeserialize<Farm>(
farmLat.X.ToString() + "_" + farmLong.Y.ToString() + ".bin"));
}
catch
{
// make me a dummy farm, crashing is for dummies
}
}
ObjectSerializer:
public void ProtoSerialize<T>(string fileName, T objectGraph)
{
using (var stream = File.Open(fileName, FileMode.Create))
{
Serializer.Serialize<T>(stream, objectGraph);
}
}
public T ProtoDeserialize<T>(string fileName)
{
T objectGraph;
using (var stream = File.Open(fileName, FileMode.Open))
{
objectGraph = Serializer.Deserialize<T>(stream);
}
return objectGraph;
}
protobuf-net can be configured in many different ways. By default, it creates objects via a parameterless constructor, because that option that works on all frameworks. In this usage, it is a bit like XmlSerializer. Because your types don't have a constructor, that usage can't work. The simplest option is to add a parameterless constructor. For use on the full framework this does not need to be public - so a private / protected etc constructor is fine - but note that this (private / protected) won't work on Silverlight etc.
The next option is to skip the constructor completely - a lot like DataContractSerializer. This can be done via attributes or via runtime configuration of the type-model. To illustrate the first:
[ProtoContract(SkipConstructor = true)]
public class Foo {...}
Again - this works great on most frameworks, but there are a few where it doesn't work (the framework utility method to do it simply doesn't exist).
Finally, you can provide your own factory methods; either per-type of globally. This factory method is a static method that returns a vanilla instance (optionally accepting things like the serialization-context, requested-type, etc). In addition to providing complete control over construction, this is also useful if you want to provide object-pooling etc. This option works on all frameworks, but requires you to write extra code and configuration.
One of the requirements of protobuf-net is that a default constructor be provided.
Because both Farm and Pasture objects' constructors contain parameters. There is no longer a default constructor. You must provide one.
Add one to get your data back.
Farm() {}
Pastures() {}
Related
I am working on accounting software, and I need help with generics.
I have multiple document types and depending on the type different posting rules will apply.
Now my question is how do I make everything generic?
I have tried this
public interface IDocument<IItem>
{
public Guid Id {get;set;}
public DocumentType DocumentType {get;set;} // enum
public List<IItem> Items {get;set;}
}
public interface IItem
{
public Guid Id {get;set;}
public double Net {get;set;}
public double Vat {get;set;}
public double Gross {get;set;}
}
public class PostDocument
{
public bool Post(IDocument<IItem> document)
{
foreach(item in document.Items)
{
// do something
}
}
}
The difficult thing here is that I will have multiple item classes because items for wholesale or retail are not the same(But every item class will have some common properties such as Net, Vat, and Gross). How would I get around it so I can have this generic method for all document types, so I don't have to write a method for every document type that my application will have?
If you don't expect to have thousands of document types, a reasonable way to approach this problem is to use the strategy pattern. There are a few different ways to approach the problem, but in a nutshell it is designed to swap one algorithm with another that has the same interface.
The pattern can also be used to switch between services that have a similar purpose based on a model object, similar to your IDocument<IItem> type.
Assumptions
You want to adhere to the Single Responsiblity Principle and Open/Closed Principle and take advantage of .NET generics.
It is certainly possible to achieve the following using casting, but it would involve modifying multiple types virtually every time you change something.
Document and Item Interfaces
First of all, we need some rework of the interfaces. For the design to work, we need both a generic and non-generic document interface. This gets us out of a common pitfall with trying to specify the closing type in places where it is not allowed, since C# generics are very strict and don't allow wildcards like some other languages do.
public interface IDocument
{
public Guid Id { get; set; }
}
public interface IDocument<TItem> : IDocument
where TItem : IItem
{
public IList<TItem> Items { get; set; }
}
public interface IItem
{
public Guid Id { get; set; }
public decimal Net { get; set; }
public decimal Vat { get; set; }
public decimal Gross { get; set; }
}
We declare IDocument<TItem> : IDocument where TItem : IItem to ensure that the properties of the concrete implementation of IItem are available to the rest of our code without casting.
Note the DocumentType enum was removed, as it is not necessary and redundant since we can check a document type as follows:
IDocument document = new RetailDocument();
if (document is RetailDocument retailDocument)
// do something with retailDocument
You can add it back if you feel you need it, though.
Concrete Document and Item Implementations
As others have pointed out in the comments, when dealing with currency we typically declare the type as decimal instead of double.
public class RetailItem : IItem
{
public Guid Id { get; set; }
public decimal Net { get; set; }
public decimal Vat { get; set; }
public decimal Gross { get; set; }
// Other properties
public string RetailStuff { get; set; }
}
public class WholesaleItem : IItem
{
public Guid Id { get; set; }
public decimal Net { get; set; }
public decimal Vat { get; set; }
public decimal Gross { get; set; }
// Other properties
public string WholesaleStuff { get; set; }
}
public class RetailDocument : IDocument<RetailItem>
{
public Guid Id { get; set; }
public IList<RetailItem> Items { get; set; }
}
public class WholesaleDocument : IDocument<WholesaleItem>
{
public Guid Id { get; set; }
public IList<WholesaleItem> Items { get; set; }
}
DocumentStrategy and DocumentPoster Interfaces
To make your strategy classes (the part that handles the post) generic, we need an interface for it. We also provide an (optional) IDocumentPoster interface, which would come in handy if you are using dependency injection.
PostDocument was named DocumentPoster because in C# we name methods after verbs and classes/properties after nouns.
public interface IDocumentStrategy
{
bool Post<TDocument>(TDocument document) where TDocument : IDocument;
bool AppliesTo(IDocument document);
}
public interface IDocumentPoster
{
bool Post(IDocument document);
}
DocumentStrategy Abstraction
Here is an abstract class that is used to hide the ugly details of casting to the concrete IDocument type so we can access the strongly-typed properties within the strategy implementations.
public abstract class DocumentStrategy<TDoc> : IDocumentStrategy
{
bool IDocumentStrategy.AppliesTo(IDocument document)
{
// Map the RetailDocument to this strategy instance
return document is TDoc;
}
bool IDocumentStrategy.Post<TDocument>(TDocument document)
{
return Post((TDoc)(object)document);
}
protected abstract bool Post(TDoc document);
}
Concrete Document Strategy Implementations
public class RetailDocumentStrategy : DocumentStrategy<RetailDocument>
{
protected override bool Post(RetailDocument document)
{
// Post RetailDocument...
// Note that all of the properties of RetailDocument will be avalable here.
//var x = document.Items[0].RetailStuff;
return true;
}
}
public class WholesaleDocumentStrategy : DocumentStrategy<WholesaleDocument>
{
protected override bool Post(WholesaleDocument document)
{
// Post WholesaleDocument...
// Note that all of the properties of WholesaleDocument will be avalable here.
//var x = document.Items[0].WholesaleStuff;
return true;
}
}
NOTE: You specified you don't want to write a method for every document type, but you sort of have to if you have different properties that you are reading in each case. If you have any common processing code that you want to share between your strategy implementations, it is usually better to inject a service that handles the common functionality into the constructor of the strategies than to put the common code in DocumentStrategy<TDoc>. That way, if you have a new strategy that doesn't use the common functionality, you can simply omit the injection on that one class.
var documentPosterService = new DocumentPosterService();
var strategies = new IDocumentStrategy[]
{
new RetailStrategy(documentPosterService),
new WholesaleStrategy(documentPosterService)
};
See the Usage section below to get an idea how to wire these strategies up. Note I am not showing the modifications to the RetailStrategy and WholesaleStrategy classes you will need to make to accept the extra service parameter, but it is similar to the DocumentPoster class below.
DocumentPoster Implementation
Here is the class that ties it all together. Its main purpose is to select the strategy based on the type of document that is being passed to it before delegating the task of processing the document to the strategy.
We provide the strategy implementations through the constructor so we can add/remove document strategies later without needing to change existing strategy implementations or DocumentPoster.
public class DocumentPoster : IDocumentPoster
{
private readonly IEnumerable<IDocumentStrategy> documentStrategies;
public DocumentPoster(IEnumerable<IDocumentStrategy> documentStrategies)
{
this.documentStrategies = documentStrategies
?? throw new ArgumentNullException(nameof(documentStrategies));
}
public bool Post(IDocument document)
{
return GetStrategy(document).Post(document);
}
private IDocumentStrategy GetStrategy(IDocument document)
{
var strategy = documentStrategies.FirstOrDefault(s => s.AppliesTo(document));
if (strategy is null)
throw new InvalidOperationException(
$"Strategy for {document.GetType()} not registered.");
return strategy;
}
}
Usage
var poster = new DocumentPoster(
new IDocumentStrategy[] {
new RetailDocumentStrategy(),
new WholesaleDocumentStrategy()
});
var retailDocument = new RetailDocument()
{
Id = Guid.NewGuid(),
Items = new List<RetailItem>
{
new RetailItem() { Id = Guid.NewGuid(), Net = 1.1m, Gross = 2.2m, Vat = 3.3m, RetailStuff = "foo" },
new RetailItem() { Id = Guid.NewGuid(), Net = 1.2m, Gross = 2.3m, Vat = 3.4m, RetailStuff = "bar" },
}
};
poster.Post(retailDocument);
var wholesaleDocument = new WholesaleDocument()
{
Id = Guid.NewGuid(),
Items = new List<WholesaleItem>
{
new WholesaleItem() { Id = Guid.NewGuid(), Net = 2.1m, Gross = 3.2m, Vat = 4.3m, WholesaleStuff = "baz" },
new WholesaleItem() { Id = Guid.NewGuid(), Net = 3.2m, Gross = 4.3m, Vat = 5.4m, WholesaleStuff = "port" },
}
};
poster.Post(wholesaleDocument);
So I'm building a small game framework to reinforce abstract classes and interfaces. I've been building classes for some mechanics and I'm not sure how to deal with the final pieces.
Here's the class framework (with some miscellaneous methods removed):
public abstract class Ability
{
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual string Type { get; set; }
public virtual string Stat { get; set; }
public virtual float Scale { get; set; }
public virtual float MPCost { get; set; }
public virtual float SPCost { get; set; }
}
public class Attack : Ability
{
public float BaseDmg { get; set; }
public bool isUnblockable { get; set; }
public float GetDmg(int stat)
{
return BaseDmg * (1 + stat * Scale);
}
}
Now for the actual Attacks I want to create, should I instantiate like I have been?
public static class AllAttacks
{
//Physical Attacks
public static Attack slash = new Attack();
//Magical Attacks
public static Attack push = new Attack();
public static void Generate()
{
//Physical Attacks
slash.Name = "Slash";
slash.Description = "A simple but effective strike.";
slash.Type = "physical";
slash.Stat = "str";
slash.Scale = 0.1F;
slash.MPCost = 0;
slash.SPCost = 1;
slash.BaseDmg = 5;
slash.isUnblockable = false;
//Magical Attacks
push.Name = "Push";
push.Description = "A powerful telekinetic strike.";
push.Type = "magic";
push.Stat = "int";
push.Scale = 0.1F;
push.MPCost = 1;
push.SPCost = 0;
push.BaseDmg = 5F;
push.isUnblockable = false;
}
Or should I actually create a new inherited class for each unique item and then instantiate them in the Fight class? And if I do, should these be static or non-static?
public class Slash : Attack
{
//Code Here
}
Could anyone point me to best practices or what the most efficient method would be?
Typically there are two main reasons to define a new class: new behavior and/or new contract, that is changed implementation for the former reason or added new public members for the latter. Now considering your example I don't see changed contracts or behaviors for various types of attack (only changed state) so I don't see a reason to define new classes for them. From the readability standpoint the Generate method isn't optimal however - I'd create separate methods for different types of attack which would clearly denote what type of attack they create.
As for the instantiation aspects, if you aren't going to mutate your attack instances than get them created in the single place is pretty okay, otherwise you need to control the life cycle of every attack instance on the level where the instance is used.
I have a series of classes used to represent identifiers in my project that are supposed to have a specific string storage format. I don't have control on this format.
The classes are pure containers, they don't do anything. The storage format is of the form "CLASSSTYPE|key1|key2|key3|...|keyN". Each "key" can be mapped to one property of the class.
Right now the FromStorageString and ToStorageString functions look like this:
public class SomeTypeId : IObjectId
{
public static string ToStorageString(SomeTypeId id)
{
return string.Format("{0}|{1}|{2}", typeof(SomeTypeId).Name, MyIntKey, MyStringKey);
}
public static SomeTypeId FromStorageString(IdReader source)
{
int intKey = source.Retrieve<int>();
string stringKey = source.Retrieve<string>();
return new SomeTypeId(intKey, stringKey);
}
public int MyIntKey { get; private set; }
public string MyStringKey { get; private set; }
public SomeTypeId(int intKey, string stringKey)
{
MyIntKey = intKey;
MyStringKey = stringKey;
}
}
We are checking the From/To consistency in unit tests, but I feel there should be a way to simplify the set up and perform the check a compile-time.
What I had in mind is something like this:
[Storage("MyIntKey", "MyStringKey")]
public class SomeTypeId : IObjectId
{
private SomeTypeId() {}
public int MyIntKey { get; private set; }
public string MyStringKey { get; private set; }
public SomeTypeId(int intKey, string stringKey)
{
MyIntKey = intKey;
MyStringKey = stringKey;
}
}
But first I don't know how to do this with the no parameters constructor and the property setters staying private. I am reluctant to have them public.
Second this approach is not robust to property name change and typos because the property names in the attribute are strings.
Should I expose the setters and private constructor ?
Is there a better way of doing this ?
I'm using RestSharp to deserialize some data. This works fine and all the data loads correctly, however one of my fields is in csv format (I can't change this unfortunately) and I need to parse out that data and load it into my object.
What is a good way to structure my class so that the code inside loadData() is run when the "data" object receives a value? I basically want to avoid running loadData on its own before I can use every object, as my SkaterData object is created 20+ times.
This is what my class structure looks like:
public class SkaterData
{
public int id { get; set; }
public string data { get; set; }
public PlayerData PlayerData { get; set; }
public void loadData()
{
var dataRows = data.Split(',');
PlayerData = new PlayerData(Int32.Parse(dataRows[0]), dataRows[1], dataRows[2]);
}
}
public class PlayerData
{
public int Number { get; set; }
public string Position { get; set; }
public string Name { get; set; }
public PlayerData(int number, string position, string name)
{
this.Name = name;
this.Position = position;
this.Number = number;
}
}
Both getters and setters are functions which means that you can write something like this:
private string _data;
public int id { get; set; }
public string data
{
get { return _data; }
set
{
_data = value;
loadData();
}
}
public PlayerData PlayerData { get; set; }
public void loadData()
{
var dataRows = data.Split(',');
PlayerData = new PlayerData(Int32.Parse(dataRows[0]), dataRows[1], dataRows[2]);
}
In the above code sample, I explicitly defined the data property (which the syntax you were using is just sytantic sugar for). I then added calling loadData to the setter of this property.
Since that setter will get called on deserialization (probably) you may need a slightly different variant of what I have, but its hard to say what it would be based on your problem statement.
I am pretty sure this is what OnDeserializedAttribute is for, but I have not been able to get it to work using an XmlSerializer (edit: that's because it doesn't implement it I guess).
[OnDeserialized()]
internal void OnDeserializedMethod(StreamingContext context)
{
}
I'm trying to determine how to address this use case using protobuf-net (Marc Gravell's implementation).
We have class A, which is considered version 1
An instance of class A has been serialized to disk
We now have class B, which is considered version 2 of class A (there were so many things wrong with class A, we had to create class B for the next version). Class A still exists in code, but only for legacy purposes.
I want to deserialize the version:1 data (stored to disk) as a class B instance, and use a logic routine to translate the data from the previous class A instance to a new instance of class B.
The instance of class B will be serialized to disk during operation.
The application should expect to deserialize instances of both class A and B.
The concept of data contract surrogates and the DataContractSerializer come to mind. The goal is transition the version:1 data to the new class B structure.
An example:
[DataContract]
public class A {
public A(){}
[DataMember]
public bool IsActive {get;set;]
[DataMember]
public int VersionNumber {
get { return 1; }
set { }
}
[DataMember]
public int TimeInSeconds {get;set;}
[DataMember]
public string Name {get;set;}
[DataMember]
public CustomObject CustomObj {get;set;} //Also a DataContract
[DataMember]
public List<ComplexThing> ComplexThings {get;set;} //Also a DataContract
...
}
[DataContract]
public class B {
public B(A a) {
this.Enabled = a.IsActive; //Property now has a different name
this.TimeInMilliseconds = a.TimeInSeconds * 1000; //Property requires math for correctness
this.Name = a.Name;
this.CustomObject2 = new CustomObject2(a.CustomObj); //Reference objects change, too
this.ComplexThings = new List<ComplexThings>();
this.ComplexThings.AddRange(a.ComplexThings);
...
}
public B(){}
[DataMember]
public bool Enabled {get;set;]
[DataMember]
public int Version {
get { return 2; }
set { }
}
[DataMember]
public double TimeInMilliseconds {get;set;}
[DataMember]
public string Name {get;set;}
[DataMember]
public CustomObject2 CustomObject {get;set;} //Also a DataContract
[DataMember]
public List<ComplexThing> ComplexThings {get;set;} //Also a DataContract
...
}
Class A was the first iteration of our object, and is actively in use. Data exists in v1 format, using class A for serialization.
After realizing the error of our ways, we create a new structure called class B. There are so many changes between A and B that we feel it's better to create B, as opposed to adapting the original class A.
But our application already exists and class A is being used to serialize data. We're ready to roll our changes out to the world, but we must first deserialize data created under version 1 (using class A) and instantiate it as class B. The data is significant enough that we can't just assume defaults in class B for missing data, but rather we must transition the data from a class A instance to class B. Once we have a class B instance, the application will serialize that data again in class B format (version 2).
We're assuming we'll make modifications to class B in the future, and we want to be able to iterate to a version 3, perhaps in a new class "C". We have two goals: address data already in existence, and prepare our objects for future migration.
The existing "transition" attributes (OnSerializing/OnSerialized,OnDeserializing/OnDeserialized,etc.) don't provide access to the previous data.
What is the expected practice when using protobuf-net in this scenario?
Right; looking at them you have indeed completely changed the contract. I know of no contract-based serializer that is going to love you for that, and protobuf-net is no different. If you already had a root node, you could do something like (in pseudo-code):
[Contract]
class Wrapper {
[Member] public A A {get;set;}
[Member] public B B {get;set;}
[Member] public C C {get;set;}
}
and just pick whichever of A/B/C is non-null, perhaps adding some conversion operators between them. However, if you just have a naked A in the old data, this gets hard. There are two approaches I can think of:
add lots of shim properties for compatibility; not very maintainable, and I don't recommend it
sniff the Version as an initial step, and tell the serializer what to expect.
For example, you could do:
int version = -1;
using(var reader = new ProtoReader(inputStream)) {
while(reader.ReadFieldHeader() > 0) {
const int VERSION_FIELD_NUMBER = /* todo */;
if(reader.FieldNumber == VERSION_FIELD_NUMBER) {
version = reader.ReadInt32();
// optional short-circuit; we're not expecting 2 Version numbers
break;
} else {
reader.SkipField();
}
}
}
inputStream.Position = 0; // rewind before deserializing
Now you can use the serializer, telling it what version it was serialized as; either via the generic Serializer.Deserialize<T> API, or via a Type instance from the two non-generic APIs (Serializer.NonGeneric.Deserialize or RuntimeTypeModel.Default.Deserialize - either way, you get to the same place; it is really a case of whether generic or non-generic is most convenient).
Then you would need some conversion code between A / B / C - either via your own custom operators / methods, or by something like auto-mapper.
If you don't want any ProtoReader code kicking around, you could also do:
[DataContract]
class VersionStub {
[DataMember(Order=VERSION_FIELD_NUMBER)]
public int Version {get;set;}
}
and run Deserialize<VersionStub>, which will give you access to the Version, which you can then use to do the type-specific deserialize; the main difference here is that the ProtoReader code allows you to short-circuit as soon as you have a version-number.
I don't have an expected practice, but this is what I'd do.
Given you still have access to your V1 class add a property on your V1 class that provides a V2 instance.
In your ProtoAfterDeserialization of V1 create an instance of V2 and seeing it's a Migration I'd suggest manually transfer across what you need (or if it's not too hard, try Merge YMMV).
Also in your ProtoBeforeSerialization throw some form of exception so that you don't attempt to write out the old one any more.
Edit: Examples of using these (VB code)
<ProtoBeforeSerialization()>
Private Sub BeforeSerialisaton()
End Sub
<ProtoAfterSerialization()>
Private Sub AfterSerialisaton()
End Sub
<ProtoBeforeDeserialization()>
Private Sub BeforeDeserialisation()
End Sub
<ProtoAfterDeserialization()>
Private Sub AfterDeserialisation()
End Sub
after seeing your example I hope this satisfied what you are trying to do. Class1 is how you load/convert.
using ProtoBuf;
using System.Collections.Generic;
using System.IO;
public class Class1
{
public Class1()
{
using (FileStream fs = new FileStream("c:\\formatADataFile.dat",
FileMode.Open, FileAccess.Read))
{
A oldA = Serializer.Deserialize<A>(fs);
B newB = oldA.ConvertedToB;
}
}
}
[ProtoContract()]
public class B
{
public B(A a)
{
//Property now has a different name
this.Enabled = a.IsActive;
//Property requires math for correctness
this.TimeInMilliseconds = a.TimeInSeconds * 1000;
this.Name = a.Name;
//Reference objects change, too
this.CustomObject2 = new CustomObject2(a.CustomObj);
this.ComplexThings = new List<ComplexThings>();
this.ComplexThings.AddRange(a.ComplexThings);
//...
}
public B() { }
//[DataMember]
[ProtoMember(1)]
public bool Enabled { get; set; }
//[DataMember]
public int Version
{
get { return 2; }
private set { }
}
[ProtoMember(2)]
public double TimeInMilliseconds { get; set; }
[ProtoMember(3)]
public string Name { get; set; }
[ProtoMember(4)]
public CustomObject2 CustomObject { get; set; } //Also a DataContract
[ProtoMember(5)]
public List<ComplexThing> ComplexThings { get; set; } //Also a DataContract
///...
}
[ProtoContract()]
public class CustomObject2
{
public CustomObject2()
{
Something = string.Empty;
}
[ProtoMember(1)]
public string Something { get; set; }
}
[ProtoContract()]
public class A
{
public A()
{
mBConvert = new B();
}
[ProtoMember(1)]
public bool IsActive { get; set; }
[ProtoMember(2)]
public int VersionNumber
{
get { return 1; }
private set { }
}
[ProtoBeforeSerialization()]
private void NoMoreSavesForA()
{
throw new System.InvalidOperationException("Do Not Save A");
}
private B mBConvert;
[ProtoAfterDeserialization()]
private void TranslateToB()
{
mBConvert = new B(this);
}
public B ConvertedToB
{
get
{
return mBConvert;
}
}
[ProtoMember(3)]
public int TimeInSeconds { get; set; }
[ProtoMember(4)]
public string Name { get; set; }
[ProtoMember(5)]
public CustomObject CustomObj { get; set; } //Also a DataContract
[ProtoMember(6)]
public List<ComplexThing> ComplexThings { get; set; } //Also a DataContract
//...
}
[ProtoContract()]
public class CustomObject
{
public CustomObject()
{
}
[ProtoMember(1)]
public int Something { get; set; }
}
[ProtoContract()]
public class ComplexThing
{
public ComplexThing()
{
}
[ProtoMember(1)]
public int SomeOtherThing { get; set; }
}