Delphi:使用枚举器过滤TList按班级类型?
好吧,这可能会令人困惑。我想做的是使用枚举器仅返回基于类类型的通用列表中的某些项目。
给定以下层次结构:
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;
我尝试使用 Primoz Gabrijelcic 的改编版本扩展 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;
但是,当我尝试编译 Foo
时,Delphi 2010 抛出关于不兼容类型:TCircle 和 TShape
的错误。如果我注释掉 TCircle
循环,那么我会收到关于 Tquare
的类似错误。如果我也注释 TSquare
循环,则代码将编译并运行。嗯,它的工作原理是枚举每个对象,因为它们都源自 TShape
。奇怪的是,编译器指示的行号比我的文件末尾多了 2 行。在我的演示项目中,它指示第 177 行,但实际上只有 175 行。
有什么办法可以让这个工作吗?我希望能够直接分配给 Circle,而无需进行任何类型转换或检查我的 for
循环本身。
Okay, this might be confusing. What I'm trying to do is use an enumerator to only return certain items in a generic list based on class type.
Given the following hierarchy:
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;
How can I extend TShapeList
such that I can do something similar to the following:
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;
I've tried extending TShapeList
using an adapted version of Primoz Gabrijelcic's bit on Parameterized Enumerators using a factory record as follows:
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;
Then I modified Foo
to be:
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;
However, Delphi 2010 is throwing an error when I try to compile Foo
about Incompatible types: TCircle and TShape
. If I comment out the TCircle
loop, then I get a similar error about TSquare
. If I comment the TSquare
loop out as well, the code compiles and works. Well, it works in the sense that it enumerates every object since they all descend from TShape
. The strange thing is that the line number that the compiler indicates is 2 lines beyond the end of my file. In my demo project, it indicated line 177, but there's only 175 lines.
Is there any way to make this work? I'd like to be able to assign to Circle directly without going through any typecasts or checking in my for
loop itself.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您没有在此处显示它,但问题可能在于 GetCurrent 的实现。
虽然编译器接受类似于
泛型类中的内容,但当结果类不等于TShape时,这将失败,TCircle和TSquare<就是这种情况/em>。这就是它在第三个循环中起作用的原因。
将代码更改为
,就可以了。
错误的不存在行号是由于解析编译器完成的内部泛型而导致的,该内部泛型不能很好地映射到行号。
You don't show it here, but the problem probably lies in the implementation of GetCurrent.
While the compiler accepts something like
in the generic class, this will fail when the result class doesn't equal TShape, which is the case for TCircle and TSquare. That's why it works in the third loop.
Change the code to
and you are fine.
The non-existing line number for the error is due to resolving the internal generic done by the compiler which doesn't map well to line numbers.
您不能直接在枚举器中执行此操作。恐怕你被“是”困住了。唯一的方法确实是创建一些辅助枚举器。在您的情况下,请尝试注释掉行,直到找到唯一使编译器不满意的行。
抱歉,我只能建议这么多。
You can't do that directly in the enumerator. I'm afraid you're stuck with "is". The only way is indeed to create some helper enumerator. In your case try commenting out lines until you find the only that's making the compiler unhappy.
Sorry that's quite all I can suggest.
您在问题中写下了答案:)
您只需调用正确的函数:
关于您的代码的一个小注释:您可以转储您的
TShapeFilterFactory
记录,然后只需添加一个方法:You wrote the answer in your question :)
You just have to call the right function :
One small remark about your code : you can dump your
TShapeFilterFactory
record, and simply add a method :