Today I ran into the need to generate a checksum for a class type. I’m working on a project where if the class definition changes down the road, the application needs to know it changed and behave accordingly.
In order to do this I needed to be able to gather info about the class and generate a checksum that would let me quickly check if the class had changed in the future. This is an example of where the new Delphi 2010 RTTI really shines. The link is for Robert Love’s articles on Delphi’s newer RTTI features and those articles helped me get under the hood and understand them.
In this example, I’m gathering info on the class name, it’s properties, fields, methods and attributes – likely overkill. You could gather more info, or less, depending on your needs. The goal here is to generate one giant input string to the SHA1FromString function that will likely change if anything about the class changes.
The SHA1 functionality comes from the Indy library. Thanks to Zarko for pointing this out!
program ClassChecksum;
{$APPTYPE CONSOLE}
uses
SysUtils,
IdHashSHA,
Rtti;
type
TPerson = class(TObject)
private
FBirthdate: TDate;
FName: string;
FPhone: string;
public
property Birthdate: TDate read FBirthdate write FBirthdate;
property Name: string read FName write FName;
property Phone: string read FPhone write FPhone;
end;
function SHA1FromString(const AString: string): string;
var
SHA1: TIdHashSHA1;
begin
SHA1 := TIdHashSHA1.Create;
try
Result := SHA1.HashStringAsHex(AString);
finally
SHA1.Free;
end;
end;
function GenerateClassChecksum(AClass: TClass): string;
var
Attribute: TCustomAttribute;
Context: TRttiContext;
Field: TRttiField;
InputStr: string;
Method: TRttiMethod;
Prop: TRttiProperty;
RttiType: TRttiType;
begin
Context := TRttiContext.Create;
try
RttiType := Context.GetType(AClass);
{ Start with the class name }
InputStr := AClass.ClassName;
{ Walk the properties }
for Prop in RttiType.GetProperties do
InputStr := InputStr + Prop.Name;
{ Walk the fields }
for Field in RttiType.GetFields do
InputStr := InputStr + Field.Name;
{ Walk the methods }
for Method in RttiType.GetMethods do
InputStr := InputStr + Method.Name;
{ Walk the attributes }
for Attribute in RttiType.GetAttributes do
InputStr := InputStr + Attribute.ToString;
{ Generate and return the checksum }
Result := SHA1FromString(InputStr);
finally
Context.Free;
end;
end;
begin
Writeln('Checksum for class TPerson:');
Writeln(GenerateClassChecksum(TPerson));
Readln;
end.
Here’s the output from the program:
Checksum for class TPerson:
EC0719180793CF0A63D31C81983FF6A05A37331E
A pretty unusual need, but hopefully this will help someone. Enjoy!