1

好吧,这可能会令人困惑。我想要做的是使用枚举器只返回基于类类型的通用列表中的某些项目。

给定以下层次结构:

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器上使用Primoz Gabrijelcic 的修改版本进行扩展,如下所示:

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关于Incompatible types: TCircle and TShape. 如果我注释掉TCircle循环,那么我会收到类似的错误TSquare。如果我TSquare也将循环注释掉,代码就会编译并工作。好吧,它的工作原理是枚举每个对象,因为它们都来自TShape. 奇怪的是,编译器指示的行号是文件末尾之外的 2 行。在我的演示项目中,它表示第 177 行,但只有 175 行。

有什么办法可以使这项工作?我希望能够直接分配给 Circle 而无需经过任何类型转换或检查我的for循环本身。

4

3 回答 3

3

您没有在这里展示它,但问题可能在于GetCurrent的实现。

虽然编译器接受类似

result := FShapeList[FIndex];

在泛型类中,当结果类不等于TShape时,这将失败, TCircleTSquare就是这种情况。这就是它在第三个循环中起作用的原因。

将代码更改为

result := T(FShapeList[FIndex]);

你很好。

错误的不存在行号是由于解析了编译器完成的内部泛型,它不能很好地映射到行号。

于 2010-04-30T15:25:43.120 回答
0

您不能直接在枚举器中执行此操作。恐怕你被“是”困住了。唯一的方法确实是创建一些辅助枚举器。在您的情况下,请尝试注释掉行,直到您发现唯一让编译器不满意的地方。

抱歉,我能建议的就这么多。

于 2010-04-30T06:58:25.127 回答
0

您在问题中写下了答案:)

你只需要调用正确的函数:

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;
于 2010-04-30T07:18:50.043 回答