سؤال

حسنًا ، قد يكون هذا مربكًا. ما أحاول القيام به هو استخدام العداد لإرجاع عناصر معينة فقط في قائمة عامة استنادًا إلى نوع الفصل.

بالنظر إلى التسلسل الهرمي التالي:

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;

ومع ذلك ، فإن Delphi 2010 يرمي خطأ عندما أحاول التجميع Foo حول Incompatible types: TCircle and TShape. إذا علقت TCircle حلقة ، ثم أحصل على خطأ مماثل TSquare. إذا علقت TSquare حلقة الخروج كذلك ، فإن الرمز يجمع ويعمل. حسنًا ، إنه يعمل بمعنى أنه يعدد كل كائن لأنهم جميعًا ينحدرون من TShape. الشيء الغريب هو أن رقم السطر الذي يشير إليه المترجم هو سطرين بعد نهاية ملفي. في مشروعي التجريبي ، أشار إلى السطر 177 ، ولكن هناك 175 سطرًا فقط.

هل هناك أي طريقة لجعل هذا العمل؟ أود أن أكون قادرًا على التعيين إلى Circle مباشرة دون المرور بأي من أنواع typecasts أو التحقق من ذلك for حلقة نفسها.

هل كانت مفيدة؟

المحلول

أنت لا تظهر ذلك هنا ، لكن المشكلة ربما تكمن في تنفيذ getCurrent.

بينما يقبل المترجم شيئًا مثل

result := FShapeList[FIndex];

في الفئة العامة ، سيفشل هذا عندما لا تساوي فئة النتيجة tshape, ، وهذا هو الحال tcircle و Tsquare. لهذا السبب يعمل في الحلقة الثالثة.

تغيير الرمز إلى

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;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top