How to photograph splashes every time!
6 years ago
Welcome back! We're so glad you enjoy our writing. If you especially like a particular article, please consider sharing it using the button at the bottom of each article. Thanks!
type
IIterator = interface
['{DFA2FE47-053A-4F5C-BB30-8F2A8C6936EE}']
function AtEnd: Boolean;
function NextItem: TObject;
end;The first line of the declaration says we're creating a class of type TListIterator that descends from TInterfacedObject and implements IIterator. It then says we've got two private variables: one will be used to store the current position and the other will store the list object we're iterating over. We have three methods: a constructor that takes the list as a parameter and initializes the two private variables, and implementations of the AtEnd and NextItem functions declared in the interface. All methods declared in an interface need to be implemented somewhere in the class hierarchy at or above the implementing class. Typically, they are implemented in the same class as the one implementing the interface, but they could be implemented in ancestor classes.type TListIterator = class(TInterfacedObject, IIterator) private fList: TList; fPosition: Integer; public constructor Create(const aList: TList); function AtEnd: Boolean; function NextItem: TObject; end; constructor TListIterator.Create(const aList: TList); begin inherited Create; fList := aList; fPosition := 0; end; function TListIterator.AtEnd: Boolean; begin result := fPosition >= fList.Count; end; function TListIterator.NextItem: TObject; begin result := nil; if fPosition < fList.Count then begin result := fList[aPosition]; Inc(fPosition); end; end;
Now simply doing some clean-up housekeepping.procedure TfrmInterfaces101.AfterConstruction; var i: Integer; begin inherited; cList := TObjectList.Create; cList.OwnsObjects := True; for i := 1 to 10 do cList.Add(TObject.Create); end;
Now the use of the iterator.procedure TfrmInterfaces101.BeforeDestruction; begin inherited; cList.Free; end;
procedure TfrmInterfaces101.btnTestClick(Sender: TObject);
var
lIterator: IIterator;
begin
lIterator := TListIterator.Create(cList);
while not lIterator.AtEnd do
memoResults.Lines.Add(Format('%p', [Pointer(lIterator.NextItem)]));
end;Just as if normal class types are used, this method first declares a variable of the IIterator type. Next the variable is assigned a new instance of a class. This is where the first difference can be noticed; the object is instantiated with the TListIterator class but it's assigned to a variable of a different type. Objects that implement an interface, either directly or in an ancestor, can be directly assigned to variables of that interface type. Next there's a loop that uses the two methods of the interface to get each item in the list.function someIntFunction: integer;What does ShowMessage show?
begin
if false then
result := 20;
end;
procedure someIntMethod;
var
lValue: integer;
begin
lValue := 10;
lValue := someIntFunction;
ShowMessage(IntToStr(lValue));
end;
When this code is compiled, a compiler warning is issued for someIntFunction indicating that the result may be undefined. This is a valid warning and what is displayed will be whatever happens to be on the stack; some random value.
Now consider this:
function someStringFunction: string;When this is compiled, there is no warning for someStringFunction. A warning should probably also be emitted, but it's not. The effect however is a bit different. In this case, the variable that the result is assigned to is unchanged. This seems to occur for any type that is reference counted: strings, dynamic arrays and interfaces.
begin
if false then
result := '20';
end;
procedure someStringMethod;
var
lValue: string;
begin
lValue := '10';
lValue := someStringFunction;
ShowMessage(lValue);
end;
property NameOfProp: TypeOfProp read PropReader write PropWriter;where:
NameOfProp is the name of the property accessed in the Object Inspector and in code.Notice the two options for readers and writers. They can be either a variable or a method. Some properties directly access a variable whereas others are the result of code executing. Those properties that fall into the first class can be evaluated without side effects in the watch window, since it looks at memory. Those properties that fall into the second class cannot be evaluated in the watch window without potentially causing side effects.
TypeOfProp is the type of the property, e.g. Integer, String, TStrings.
PropReader is either a variable of type TypeOfProp or a function with a return type of TypeOfProp.
PropWriter is either a variable of type TypeOfProp or a procedure with a parameter of type TypeOfProp.