The problem is described by means of an example.
I have an abstract base class Box,
abstract class Box
{
public Box(double panelThickness) :
this(IDGenerator.GetNewID(), panelThickness)
{ }
protected Box(int id, double panelThickness)
{
ID = id;
PanelThickness = panelThickness;
}
public int ID { get; }
public double PanelThickness { get; }
}
an inherited class RectangularBox
class RectangularBox : Box
{
private static double _rectPanelThickness = 0.2;
public RectangularBox(double xDimension, double yDimension) :
base(_rectPanelThickness)
{
// ---- Code duplication:
XDimension = xDimension;
YDimension = yDimension;
}
[JsonConstructor]
private RectangularBox(int id, double xDimension, double yDimension) :
base (id, _rectPanelThickness)
{
// ---- Code duplication:
XDimension = xDimension;
YDimension = yDimension;
}
public double XDimension { get; }
public double YDimension { get; }
}
and a simple IDGenerator:
static class IDGenerator
{
private static int _id = 0;
internal static int GetNewID()
{
_id++;
return _id;
}
}
An example can be run by means of this test method:
using Newtonsoft.Json;
[TestMethod]
public void BoxJsonDeserializationTest()
{
RectangularBox rectangularBox1 = new RectangularBox(8, 9);
JsonSerializerSettings serializationSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor
};
string boxJsonString = JsonConvert.SerializeObject
(rectangularBox1, Formatting.Indented, serializationSettings);
var rectangularBoxFromJson = JsonConvert.DeserializeObject<RectangularBox>
(boxJsonString, serializationSettings);
}
Instantiating an object of RectangularBox causes an ID to be generated in the base class and the XDimension and YDimension properties to be assigned in the public constructor of the sub-class. Two things to note here:
- Both
XDimensionandYDimensionare Get only properties. Hence, its only assignable in the constructor. - The user should not be able to generate an ID by input. This is prohibited by having a Get only ID property in the base class. If the public constructor of
RectangularBoxis used, a new ID is generated automatically. However, upon deserializingRectangularBoxfrom JSON and having a protected constructor in the base class called by the private constructor in the child class allowing the ID property to be set when deserializing from JSON (using Newtonsoft.Json.
When serializing this object to JSON and then deserializing at a later stage, a new ID should not be generated for the object, but instead the ID property be assigned from the JSON. Similarly, the XDimension and YDimension properties must also come from the JSON. Hence the reason for the [JsonConstructor] attribute over the private constructor of RectangularBox.
The problem is that I cannot find a way to get rid of the code duplication in both constructors of RectangularBox, but still maintain the ability to deserialize the Get only properties from a JSON. The properties could have private setters and marked with [JsonProperty] attributes, which would allow the properties assignments to be removed from the constructors into a separate method, but this is not desired. The user should not be allowed to change those properties once a RectangularBox object is created.
Any help would be appreciated.