Delphi: استخدام العدادات لتصفية stlist حسب نوع الفصل؟
-
02-10-2019 - |
سؤال
حسنًا ، قد يكون هذا مربكًا. ما أحاول القيام به هو استخدام العداد لإرجاع عناصر معينة فقط في قائمة عامة استنادًا إلى نوع الفصل.
بالنظر إلى التسلسل الهرمي التالي:
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;