Measurement Studio for .NET Languages

cancel
Showing results forย 
Search instead forย 
Did you mean:ย 

Inheritance and clone

Here is my code :

public class BufferedPlot : WaveformPlot
{
// ...
}
public class PlotCursor : XYCursor
{
// ...
}
etc...

Now I need to clone my objects. The Clone() method works perfectly fine... but I cannot call it from subclasses ๐Ÿ˜ž
A copy constructor would be nice ! Or a brilliant idea ?


Pipo

PS : Dont answer me "Use composition instead of inheritance". I know I should ๐Ÿ™‚
PS : damn this forum is too serious. I want a pool in my topic ! But cannot start a pool to ask for a pool o_O
0 Kudos
Message 1 of 8
(4,170 Views)
By the way :
plot = (WaveformPlot)_plotModel.Clone();
throws an exception when _plotModel has an historyCapacity > 1000

buhuhuhuhuhu ๐Ÿ˜ž I cant make anything good today.
0 Kudos
Message 2 of 8
(4,161 Views)
I agree that ICloneable does not work well with hierarchies. The blog post "Copy Constructors vs ICloneable -- Redux" has a good discussion about this. Microsoft also recognizes problems with ICloneable and is considering obsoleting it. Personally, I also like copy constructors, but the Measurement Studio .NET libraries conform to the "Design Guidelines for Class Library Developers" as much as possible, so we want to see how the design guidelines are going to change in regard to cloning so we can make the Measurement Studio .NET libraries consistent rather than adding copy constructors and potentially being inconsistent.

Below is an example of how you can implement Clone in a WaveformPlot-derived class. This example defines a derived plot called BufferedPlot, adds a single BufferSize property, then overrides Clone to return a copy of the BufferPlot.


private class BufferedPlot : WaveformPlot
{
private int _bufferSize;

private const int DefaultBufferSize = 1024;

public BufferedPlot()
{
_bufferSize = DefaultBufferSize;
}

[DefaultValue(DefaultBufferSize)]
public int BufferSize
{
get
{
return _bufferSize;
}

set
{
_bufferSize = value;
}
}

public override object Clone()
{
BufferedPlot clone = new BufferedPlot();

// Copy all properties that do not have their default values.
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(this))
{
// Only set the value if it's not its default value.
if (pd.ShouldSerializeValue(this))
{
object propertyValue = pd.GetValue(this);
pd.SetValue(clone, propertyValue);
}
}

// Copy the plot data.
double[] yData = GetYData();
if (yData.Length > 0)
clone.PlotY(yData);

return clone;
}
}


Hope this helps.

- Elton
0 Kudos
Message 3 of 8
(4,150 Views)
"By the way :
plot = (WaveformPlot)_plotModel.Clone();
throws an exception when _plotModel has an historyCapacity > 1000"


I will file a bug report about this issue so it can be fixed in a future release of Measurement Studio. This will not be an issue, though, if you use the Clone approach that I described in my previous post.

- Elton
0 Kudos
Message 4 of 8
(4,148 Views)
It helps alot and thank you for the link. However I have a few questions :

- How does your approach differ from a MemberwiseClone approach ?
- Is this the way you implemented Clone in the base classes ? (if it is not a secret :))
- foreach + eventually unboxing / boxing might not be the best / fastest way to code this. Anyway it is fine for me, I dont use Clone very often (only when the user takes a snapshop of a graph, or when I create plots using a model plot).


Pipo
0 Kudos
Message 5 of 8
(4,137 Views)
Good questions.

"- How does your approach differ from a MemberwiseClone approach ?"

The fields that hold the values of the properties are not actually on the plot object. The plot holds a reference to an internal implementation class, which holds the values of the properties. MemberwiseClone would just copy the reference to this internal implementation class and you would end up with two objects that refer to the same internal implementation object, not an object that's a copy of another.

Looking at the code again, there are a couple of improvements that can be made. First, it should not set the value if it's a read-only property. Also, it should check if the property value is cloneable and if so, clone it. Here's another version of the Clone implementation example:


public override object Clone()
{
BufferedPlot clone = new BufferedPlot();

foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(this))
{
// Only set the vlaue if it's writeable and its' not the default value.
if (!pd.IsReadOnly && pd.ShouldSerializeValue(this))
{
object propertyValue = pd.GetValue(this);

if (propertyValue is ICloneable)
propertyValue = ((ICloneable)propertyValue).Clone();

pd.SetValue(clone, propertyValue);
}
}

double[] yData = GetYData();
if (yData.Length > 0)
clone.PlotY(yData);

return clone;
}


There are probably several more improvements that can be made.

"- Is this the way you implemented Clone in the base classes ? (if it is not a secret :))"

No, this is just a simple workaround solution. The real Clone implementations work directly with the internal implementation class fields.

"- foreach + eventually unboxing / boxing might not be the best / fastest way to code this. Anyway it is fine for me, I dont use Clone very often (only when the user takes a snapshop of a graph, or when I create plots using a model plot)."

That's true. Unfortunately, it's unavoidable unless you want to write the code to explicitly handle every property. As you pointed out, though, in practice the performance difference is going to be negligible.

- Elton
0 Kudos
Message 6 of 8
(4,130 Views)
Also, it should check if the property value is cloneable and if so, clone it.

Nice idea, badly it doesn't fit well to plots because I dont want my XAxis or my YAxis to be cloned :).
0 Kudos
Message 7 of 8
(4,124 Views)
You can look at the PropertyDescriptor.PropertyType to determine if it's an axis and if so, skip it. For example:


public override object Clone()
{
BufferedPlot clone = new BufferedPlot();

foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(this))
{
if (pd.PropertyType.IsAssignableFrom(typeof(Axis)))
continue;

// Only set the vlaue if it's writeable and its' not the default value.
if (!pd.IsReadOnly && pd.ShouldSerializeValue(this))
{
object propertyValue = pd.GetValue(this);

if (propertyValue is ICloneable)
propertyValue = ((ICloneable)propertyValue).Clone();

pd.SetValue(clone, propertyValue);
}
}

double[] yData = GetYData();
if (yData.Length > 0)
clone.PlotY(yData);

return clone;
}


- Elton
0 Kudos
Message 8 of 8
(4,108 Views)