I have a TCue class with a boolean variable named Selected. There is also a TCueList class (TObjectList based) that holds the list of TCue items.
I’d like all of the processing for setting TCue.Selected to be done via the TCueList object, so as TCue items are selected and deselected the holding list can update other properties or fire an event.
Is there a way of limiting the scope of TCue.Selected so that it can only be accessed via the TCueList object via the SelectAll, ClearSelection, etc procedures I’ve added?
I want to prevent direct calls such as TCueList.Items[x].Selected := True. I realise I can just avoid using those direct calls, but is it possible to limit the scope?
I guess it depends on what scope you want it limited to. If you want to prevent users in other units from acecssing the TCue.Select property you could define it in the same unit as TCueList but set the Select property to private visibility.
unit UCue;
interface
uses
System.Generics.Collections;
type
TCue = class(TObject)
private
FSelected: Boolean;
property Selected: Boolean read FSelected write FSelected;
end;
TCueList = class(TObjectList<TCue>)
private
function GetSelected(Index: Integer): Boolean;
procedure SetSelected(Index: Integer; const Value: Boolean);
public
property Selected[Index: Integer]: Boolean read GetSelected write SetSelected;
end;
implementation
function TCueList.GetSelected(Index: Integer): Boolean;
begin
Result := Items[Index].Selected;
end;
procedure TCueList.SetSelected(Index: Integer; const Value: Boolean);
begin
Items[Index].Selected := Value;
end;
end.
program CueProject;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
UCue in 'UCue.pas';
begin
try
var CueList := TCueList.Create;
try
var cue := TCue.Create;
CueList.Add(cue);
Cuelist.Selected[0] := True;
finally
CueList.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Hi,
You could make it protected, and then use a protected hack subclass to access the member.
Not sure if this is better than just leaving it public, but it’s hidden a bit.
eg.
in CueList file
type THackCue= class(TCue);
…
THackCue(cue).Selected := true;
I guess it depends if you’re just trying to avoid someone using it inadvertently, or if you’re trying to stop someone who’s determined.
If the former, then going back to the earlier thread about interfaces, it might be interesting to have that Selected member be private, but have the class implement an interface that exposes it. “Normal” class clients won’t even see it, but casting to the interface will let you access it.
Note, if you go down this path, best to have the list hold a reference to the TCue via an interface as well (ie. so all references to Cue’s are interfaces). You can get yourself in a tangle holding a reference to the class instance and an interface at the same time.
unit UCue;
interface
type
TCue = class(TObject)
protected
FSelected : boolean;
end;
implementation
end.
unit UCueList;
interface
uses
System.Generics.Collections, UCue;
type
TCueList = class(TObjectList<TCue>)
private
function GetSelected(Index: Integer): Boolean;
procedure SetSelected(Index: Integer; const Value: Boolean);
public
property Selected[Index: Integer]: Boolean read GetSelected write SetSelected;
function AddNew : TCue;
end;
implementation
type
TSelectableCue = class(TCue)
public
property Selected : boolean read FSelected write FSelected;
end;
function TCueList.AddNew : TCue;
begin
Result := TSelectableCue.Create;
Add(Result);
end;
function TCueList.GetSelected(Index: Integer): Boolean;
begin
Result := (Items[Index] as TSelectableCue).Selected;
end;
procedure TCueList.SetSelected(Index: Integer; const Value: Boolean);
begin
(Items[Index] as TSelectableCue).Selected := Value;
end;
end.
So new TCue instances are created by calling TCueList.AddNew. If you need parameters for the TCue constructor just pass them through the AddNew function as well.
You’d also replace that direct property access to the ancestor protected TCue.FSelectable with a getter/setter that can do the TCue based processing you want.
program CueProject;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
UCue in 'UCue.pas',
UCueList in 'UCueList.pas';
begin
try
var CueList := TCueList.Create;
try
var cue := CueList.AddNew;
Cuelist.Selected[0] := True;
finally
CueList.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Unless you’re holding a long term interface reference to the list, aren’t you running the risk that the list will be freed when your temporary interface reference goes out of scope?