Хорошо, это может сбить с толку. Я пытаюсь использовать перечислитель, чтобы возвращать только определенные элементы в общем списке на основе типа класса.
Учитывая следующую иерархию:
type
TShapeClass = class of TShape;
TShape = class(TObject)
private
FId: Integer;
public
function ToString: string; override;
property Id: Integer read FId write FId;
end;
TCircle = class(TShape)
private
FDiameter: Integer;
public
property Diameter: Integer read FDiameter write FDiameter;
end;
TSquare = class(TShape)
private
FSideLength: Integer;
public
property SideLength: Integer read FSideLength write FSideLength;
end;
TShapeList = class(TObjectList<TShape>)
end;
Как мне продлить TShapeList
так, чтобы я мог сделать что-то похожее на следующее:
procedure Foo;
var
ShapeList: TShapeList;
Shape: TShape;
Circle: TCircle;
Square: TSquare;
begin
// Create ShapeList and fill with TCircles and TSquares
for Circle in ShapeList<TCircle> do begin
// do something with each TCircle in ShapeList
end;
for Square in ShapeList<TSquare> do begin
// do something with each TSquare in ShapeList
end;
for Shape in ShapeList<TShape> do begin
// do something with every object in TShapeList
end;
end;
Я пытался расширить TShapeList
, используя адаптированную версию бита Примоза Габриелчича для Параметризованных счетчиков , используя заводскую запись следующим образом:
type
TShapeList = class(TObjectList<TShape>)
public
type
TShapeFilterEnumerator<T: TShape> = record
private
FShapeList: TShapeList;
FClass: TShapeClass;
FIndex: Integer;
function GetCurrent: T;
public
constructor Create(ShapeList: TShapeList);
function MoveNext: Boolean;
property Current: T read GetCurrent;
end;
TShapeFilterFactory<T: TShape> = record
private
FShapeList: TShapeList;
public
constructor Create(ShapeList: TShapeList);
function GetEnumerator: TShapeFilterEnumerator<T>;
end;
function FilteredEnumerator<T: TShape>: TShapeFilterFactory<T>;
end;
Затем я изменил Foo
на:
procedure Foo;
var
ShapeList: TShapeList;
Shape: TShape;
Circle: TCircle;
Square: TSquare;
begin
// Create ShapeList and fill with TCircles and TSquares
for Circle in ShapeList.FilteredEnumerator<TCircle> do begin
// do something with each TCircle in ShapeList
end;
for Square in ShapeList.FilteredEnumerator<TSquare> do begin
// do something with each TSquare in ShapeList
end;
for Shape in ShapeList.FilteredEnumerator<TShape> do begin
// do something with every object in TShapeList
end;
end;
Однако Delphi 2010 выдает ошибку, когда я пытаюсь скомпилировать Foo
о Incompatible types: TCircle and TShape
. Если я закомментирую цикл TCircle
, то получаю похожую ошибку о TSquare
. Если я также прокомментирую цикл TSquare
, код компилируется и работает. Ну, это работает в том смысле, что он перечисляет каждый объект, так как все они происходят от TShape
. Странно то, что номер строки, который указывает компилятор, находится на 2 строки за концом моего файла. В моем демонстрационном проекте он обозначил строку 177, но там всего 175 строк.
Есть ли способ заставить эту работу? Я бы хотел иметь возможность присваивать Circle напрямую, без прохождения каких-либо типов или проверки самого цикла for
.