【问题标题】:Delphi: Using Enumerators to filter TList<T: class> by class type?Delphi:使用枚举器按类类型过滤 TList<T:class>?
【发布时间】:2010-04-29 21:08:13
【问题描述】:

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

给定以下层次结构:

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

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

【问题讨论】:

    标签: delphi generics filter delphi-2010 enumerator


    【解决方案1】:

    这里就不展示了,问题可能出在GetCurrent的实现上。

    虽然编译器接受类似的东西

    result := FShapeList[FIndex];
    

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

    把代码改成

    result := T(FShapeList[FIndex]);
    

    你很好。

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

    【讨论】:

    • :headdesk: 就是这样。谢谢!
    【解决方案2】:

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

    抱歉,我只能建议这么多。

    【讨论】:

    • 对不起,我试图使用辅助枚举器 (FilteredEnumerator&lt;T: TShape&gt;),我只是没有在我的问题中澄清这一点。它已更新。我的助手有什么可怕的错误吗?
    【解决方案3】:

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

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

    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;
    

    【讨论】:

    • 实际上,我确实尝试过使用for Circle in ShapeList.FilteredEnumerator&lt;TCircle&gt;。这就是编译器错误的来源。我会更新问题以使其更清楚。这就是我匆忙提出问题的结果。 :-)
    • 也感谢有关消除工厂记录的提示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-11
    • 1970-01-01
    • 2019-05-26
    • 1970-01-01
    相关资源
    最近更新 更多