Delphi:使用枚举器按类类型过滤TList <t:class =“”>?</t:>

时间:2010-04-29 21:08:13

标签: delphi generics filter delphi-2010 enumerator

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

给出以下层次结构:

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在Parameterized Enumerators使用出厂记录扩展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的{​​{1}}时,Delphi 2010会抛出错误。如果我注释掉Incompatible types: TCircle and TShape循环,那么我会收到有关TCircle的类似错误。如果我也评论TSquare循环,则代码编译并运行。嗯,它的工作原理是它枚举了每个对象,因为它们都来自TSquare。奇怪的是,编译器指示的行号超出了文件末尾的2行。在我的演示项目中,它表示第177行,但只有175行。

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

3 个答案:

答案 0 :(得分:3)

您不会在此处显示,但问题可能在于 GetCurrent 的实现。

虽然编译器接受类似

的内容
result := FShapeList[FIndex];

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

将代码更改为

result := T(FShapeList[FIndex]);

你没事。

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

答案 1 :(得分:0)

您不能直接在枚举器中执行此操作。我担心你会被“坚持”所困扰。唯一的方法是创建一些辅助枚举器。在你的情况下,尝试注释掉行,直到找到唯一使编译器不满意的行。

很抱歉,这是我所能提出的全部内容。

答案 2 :(得分: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;