Delphi: Использование перечисленных перечислений для фильтрации TLIST по типу класса?
-
02-10-2019 - |
Вопрос
Хорошо, это может быть запутано. То, что я пытаюсь сделать, это использовать перечислетель, чтобы вернуть только определенные элементы в универсальном списке на основе типа класса.
Учитывая следующую иерархию:
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 строк.
Есть ли способ сделать эту работу? Я хотел бы иметь возможность присвоить кружок напрямую, не проходя через какие-либо текстуры или проверку в моем for
сам петля.
Решение
Вы не показываете это здесь, но проблема, вероятно, заключается в реализации GetCurrent.
Пока компилятор принимает что-то вроде
result := FShapeList[FIndex];
В общем классе это не удастся, когда класс результата не равен TSHape, который имеет место для Ткси и ЦККАРА. Отказ Вот почему он работает в третьей петле.
Изменить код в
result := T(FShapeList[FIndex]);
И ты в порядке.
Не существующий номер строки для ошибки связан с разрешением внутреннего универсального образования компилятором, который не хорошо отображается номерам строк.
Другие советы
Вы не можете сделать это непосредственно в перечислении. Боюсь, ты застрял с "-". Единственный путь действительно создает некоторой помощника перечисления. В вашем случае попробуйте комментировать строки, пока не найдете единственное, что делает компилятор несчастной.
Извините, что это совсем все, что я могу предложить.
Вы написали ответ в вашем вопросе :)
Вам просто нужно позвонить правильную функцию:
for Circle in ShapeList.FilteredEnumerator<TCircle> do
//blah blah
for Square in ShapeList.FilteredEnumerator<TSquare> do
//blah blah
Одно маленькое замечание о вашем коде: вы можете бросить свой TShapeFilterFactory
Запись, и просто добавьте метод:
TShapeFilterEnumerator<T>.GetEnumerator : TShapeFilterEnumerator<T>;
begin
Result := Self;
end;