Class Instance or Inherited Class for a game? Best Practice - c#

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.

Related

How to implement a generic method with multiple types?

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);

Best practice with a possibility of a null interface object within class

I've created a class that represent a component. This component has a width,height,x-Coordinate,y-Coordinate, etc. When I manipulate the width,height,x, and y, I want to keep the logic within the class. But there is an interface object within the Component Class that has similar values. This interface can be used to talk to different types of CAD software. The Shape interface can be null though.
So my question is what would be the best approach for this? In the example below, when I change "Y", should I check for null in the shape interface? Or maybe the Component Class has event handlers and the Shape Interface should register to them. So what would be best practice for designing this approach and what would give the best performance?
Appreciate it!
public class Component
{
private double _y;
public IShape Shape { get; set; }
public string Name { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double X { get; set; }
public double Y
{
get => _y;
set
{
_y = value;
if (Shape != null) Shape.Y = value;
}
}
public void Update_Shape()
{
//used to update the Shape Interface after it is assigned
}
}
public interface IShape
{
string Name { get; set; }
double Width { get; set; }
double Height { get; set; }
double X { get; set; }
double Y { get; set; }
}
UPDATE: To give more details, my interface will be able to talk to Microsoft Visio, and AutoCad. They are only meant to be used as a visual representation of the data, the are not in control of how many shapes, or where they are positioned. So in my application, the user can move, or change width/height within the application. If they have Visio open at the time, I want it to update Visio shapes as well. If it isn't open, then it doesn't matter(it will end up being updated later on). Same goes for AutoCad.
The best practice in this situation depends on what your design goals are.
If you want to automatically update IShape and performance is critical then manually writing out your setters with a null check is going to give you both. Having an event that the IShape subscribes to causes you to have to invoke the event which is more expensive than checking null. And this has the benefit of keeping the mess inside the class as you only need to assign myComponent.X = 20;
Having an event has it's benefits. If you look up the Observer Pattern you can find lots of good reads on this. If you have more than one IShape that would subscribe to your Component, say from both Visio and AutoCad at the same time this would be the way to go.
Now in terms of performance, if you're update less than a few thousand components per second and you want cleaner code I would just call Update_Shape() when you want to synchronize the values. If you are assigning multiple values at the same time you can wrap them in an action that will automatically synchronize the values after it completes.
var c = new Component();
c.Shape = new Shape();
c.UpdateShapes(s => {
s.Height = 100;
s.Width = 100;
s.X = 5;
});
public class Component
{
public IShape Shape { get; set; }
public string Name { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double X { get; set; }
public double Y { get; set; }
public void UpdateShapes(Action<Component> update)
{
update(this);
SyncronizeShapes();
}
public void SyncronizeShapes()
{
if (Shape != null)
{
Shape.Name = Name;
Shape.Width = Width;
Shape.Height = Height;
Shape.X = X;
Shape.Y = Y;
}
}
}

C# base class have a derived class member and it loops

I have this class:
public class GameData
{
public int[,] mat { get; set; }
public int dim { get; set; }
public int goal { get; set; }
public Game game { get; set; }
public GameData()
{
game = new Game();
}
}
And this two other derived classes:
public class Game :GameData
{
private Mover m;
public Game()
{
dim = 4;
goal = 16;
mat = new int[dim, dim];
m = new Mover();
}
/*Methods*/
}
public class Mover : GameData
{
/*Methods*/
}
It loops because Game constructor calls GameData constructor and so on.
How can I do?
I am starting whit OOP programming and I am not sure this three classes are the best way to set the program. GameData contains common datas, Game contains the method to play and Mover contains a group of method used by Game
Honestly inheritance doesn't make any sense here. Inheritance is an "is a type of" relationship (in other words, is Game a type of GameData? I doubt it.
Instead use composition! Just have class hold the objects they care about. Composition is a "has a" relationship, which makes a lot more sense in your case. Honestly, I would invert the Game/GameData relationship, a Game probaly holds GameData; not the other way around. Only you know what you need though.
public class GameData
{
public int[,] mat { get; set; }
public int dim { get; set; }
public int goal { get; set; }
public Game game { get; set; }
public GameData()
{
game = new Game();
dim = 4;
goal = 16;
mat = new int[dim, dim];
}
}
public class Game
{
private Mover m;
public Game()
{
m = new Mover();
}
/*Methods*/
}
public class Mover
{
/*Methods*/
}
This sentence
public GameData()
{
game = new Game();
}
makes no sense; as GameData is the parent, is not logical to have a child inside the parent. Obviously that makes a loop, as you are creating a Game : GameData over and over with that sentence.
Think about inheritance you want, as you are using it incorrectly with Mover too.
Well you definitely have a circular reference which is a design smell. You could stop the infinite loop by checking for null:
public GameData()
{
if(game == null)
game = new Game();
}
Or pass the Game instance in to the constructor rather than creating a new one - hard to say what's correct without knowing more of your overall design.

I cannot deserialize data previously serialized successfully

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() {}

Best practice for protobuf-net, versioning and surrogate types

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; }
}

Categories