Introduction
In many applications, there is the need to track changes to the data presented to users. There are several approaches in achieving this. In this article, I cover an approach in which I use Cloning and Generics to be able to compare objects and determine if any changes have been made. The use of cloning is important because in some systems the instantiation of an object could be somewhat expensive. Cloning allows us to do a deep copy of the object already instantiated thus preventing the expensive process. Once the object is cloned, we can modify the cloned copy with the new information provided by the user and determine what changes have been made. In order to compare the objects, we need to override the Equals method from the Object
class. The challenge with cloning is that we would like to know the object type and minimize the code sections required to do the cloning. In short, we would like to create a base class from which we can clone any object type. This is where Generics can help us.
Background
To better understand this article, I recommend that the readers be somewhat familiar with the following concepts:
- Generics
- Cloning/
ICloneable
The code for this article was written using .NET 2.0.
Using the Code
The first step is to create the CloneBase
class (CloneBase.cs) which implements the ICloneable
interface. This is needed to create a copy of object. Any class that implements this interface should also have the XML attribute [Serializable]
. Not adding this attribute causes an exception to occur. This attribute must also be added to all the classes that inherit from the base class we are now creating. This takes care of the cloning part of the equation. Now, we need to be able to support different types. In order to do that, we need to declare the class as a template/generic definition by adding the generic type parameter <T>
.
[Serializable]
public class CloneBase<T> : ICloneable
{
public CloneBase() { }
...
The generic type parameter is needed because this is what is used to replace where the concrete type would be. In other words, when we need to return a clone of a certain type, T
is replaced by such type. We will be able to see this with the implementation of the Clone
method. Notice how this method has a return type of T
.
public T Clone()
{
T clone = default(T);
try
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream memStream = new MemoryStream();
bf.Serialize(memStream, this);
memStream.Flush();
memStream.Position = 0;
clone = ((T)bf.Deserialize(memStream));
}
catch (Exception ex)
{
m_status = ex.Message;
}
return clone;
}
This code serializes the object into a memory stream, which creates a copy of the object. It then deserializes the generic object by using the line of code below. We can notice the casting done using the generic type (T)
. The new object is then returned.
clone = ((T)bf.Deserialize(memStream));
We can now take a look at the client code which uses this base class. The project associated with this article has a Product
class (Product.cs), which is used to represent a generic product. The class inherits from the CloneBase
base class. Notice how this class too must have the XML attribute [Serializable]
. This class also overrides the Equals
method from the Object
class. This is done to implement our own object comparison logic. In this case, we are comparing the value of all the data members. If any of the values is not the same, the code returns false
.
[Serializable]
class Product : CloneBase<Product>
{
public override bool Equals(object obj)
{
bool bRtn = true;
if (obj != null)
{
Product objRef = (Product)obj;
if (this.Name != objRef.Name ||
this.Description != objRef.Description ||
this.Sku != objRef.Sku || this.Count != objRef.Count)
bRtn = false;
}
else
bRtn = false;
return bRtn;
}
}
We can now take a look at Windows form created for this article. This simple form displays all the data values for a Product
object. When pressing the Save button, the application creates a clone of the object, assigns the values from the form to the clone, and compares the original object with the clone.
private void btnSave_Click(object sender, EventArgs e)
{
Product clone = _prod.Clone();
if (clone != null)
{
UIValues(clone, false);
if (_prod.Equals(clone) == true)
MessageBox.Show("There is no change", this.Text);
else
MessageBox.Show("There is a change", this.Text);
}
else
{
MessageBox.Show(_prod.Status, this.Text);
}
}
Points of Interest
I have found the use of generics to be very useful. This was a great new feature of .NET 2.0. There are also many generic list classes which we can use to eliminate the use of classes like the ArrayList
. I hope this article is useful to some of you. I look forward to getting your feedback.
History
- 0g06012006: Initial version