Example 9.3 Deep copies and the Assign method
As a final example in this part, well look at Delphis convention for making deep copies. Making shallow copies is easy: we simply declare a reference of the appropriate type and then assign this to the object being copied through an assignment statement. To facilitate making deep copies, Delphi provides the virtual method Assign in the TPersistent class (which is derived directly from TObject). Assign is a more sophisticated version of our CopyFrom method and well modify our previous example to use it.unit CustomerU;Before we look at the details of the Assign method, test it by entering this version and then changing TSale to use Assign. This means substituting Assign for CopyFrom in TSales Create method (ie ex 9.2 step 3 becomes FCustomer.Assign (ACustomer) ;).
interface
uses Classes;
type
TCustomer = class(TPersistent)
private
FPhoneNo: string;
FName: string;
public
property Name: string read FName write FName;
property PhoneNo: string read FPhoneNo write FPhoneNo;
procedure Assign (aCustomer: TPersistent) ; override;
end;
implementation
{ TCustomer }
procedure TCustomer.Assign(aCustomer: TPersistent) ;
begin
if aCustomer is TCustomer then
begin
Name := TCustomer(aCustomer).Name;
PhoneNo := TCustomer(aCustomer).PhoneNo;
end
else
inherited Assign (aCustomer) ;
end;
end.
If we compare this code with the previous version of TCustomer (ex 9.2 step 2) we see a number of changes. TCustomer is now derived from TPersistent rather than TObject.
TPersistent is defined in unit Classes and so we include that in the Uses clause. Instead of procedure CopyFrom we now have procedure Assign, with a parameter of type TPersistent and not of TCustomer. This overrides the virtual Assign procedure in TPersistent and so we declare it with the override keyword.
The CopyFrom method had two program statements. But Assign has quite a bit of additional code that makes it much more general than CopyFrom, so well go through this method line by line. First we see that the source parameter, aCustomer, is now of type TPersistent.
This allows polymorphism and gives Assign the potential to accept classes other than its own class, TCustomer, if they are derived from TPersistent. We assign the appropriate values should the incoming parameter be a TCustomer. Since the aCustomer parameter is declared as a TPersistent, we perform the necessary typecasting before they can assign the values. Should we want Assign to accept other TPersistent-derived classes in addition to TCustomer, we add further else if statements for each additional class.
Finally, if the type of the parameter does not match any of the types catered for in the else if clauses, we invoke the inherited Assign method.
The inherited Assign is called only when the destination object cannot perform the deep copy, ie if the else ifs do not cater for the incoming parameter type. In this case Assign in TPersistent calls the AssignTo method of the source object. The AssignTo method swaps around the order of the source and destination classes to cater for the possibility that the source object can perform the deep copy. If it can, all is well. If not, it raises an exception and the deep copy has failed.
AssignTo is not needed in this case since for this example the source and destination classes are always the same, ie always TCustomers. However, for interests sake, we include the AssignTo method below to illustrate the principle involved. (AssignTo is declared as Protected in TPersistent.)
procedure TCustomer.AssignTo(aCustomer: TPersistent) ;We can test the AssignTo method by commenting out lines and so force Assign to fail. The program still works as before without changing TSale because TPersistents Assign automatically calls TCustomers AssignTo to perform the required copying. (You can verify this by placing a breakpoint on the first statement in Assign and then single stepping through the code until you have executed the AssignTo.) In passing, in these two methods we have the inherited keyword as part of an Else statement, so invoking the superclasss method only under certain conditions.
begin
if aCustomer is TCustomer then
begin
TCustomer(aCustomer).Name := Name;
TCustomer(aCustomer).PhoneNo := PhoneNo;
end
else
// will raise an exception
inherited AssignTo (aCustomer) ;
end;
Let's sum this chapter, ..., on th next page...

