I have an existing class that has an existing method that allows you to pass a list of things to it:
TContoso = class(TSkyrim) public procedure AddObjects(Objects: TList); end;
So, in the previous moments, someone could pass a TList or TObjectList method to a method:
var list: TList; list := TObjectList.Create(True); contoso.AddObjects(list);
It didn't matter, because TObjectList was a TList . My method was flexible; he can accept and.
Now at times
Now in the following moments I prefer printed lists:
var list: TList<TGrobber>; list := TObjectList<TGrobber>.Create(True); contoso.AddObjects(list);
Of course, this does not compile, since neither TList<T> nor TObjectList<T> come with TList . This is not such a problem. I intuitively understand that I really don't need a TList , I just need something "enumerated":
Based on my experience with .NET FCL, this means that I just need to declare an IEnumerable parameter, because everything is enumerable:
IEnumerable<T> comes from IEnumerableICollection comes from IEnumerableICollection<T> comes from IEnumerableIList comes from IEnumerableIList<T> comes from IEnumerableList comes from IEnumerableList<T> comes from IEnumerable
So, I would do something like:
TContoso = class(TSkyrim) public procedure AddObjects(Objects: IEnumerable); end;
Except that Delphi BCL does not allow the polymorphism that the .NET world allows; things that are listed do not implement the IEnumerable interface:
TList = class(TObject) public function GetEnumerator: TListEnumerator; end; TObjectList = class(TList); TList<T> = class(TEnumerable<T>) public function GetEnumerator: TEnumerator<T>; end; TObjectList<T> = class(TList<T>);
Without input, how does the compiler know the type, can I list it?
Delphi uses secret encoded magic. :
the class or interface must implement the prescribed collection template. A type that implements a collection template must have the following attributes: - The class or interface must contain an open instance method called GetEnumerator() . The GetEnumerator() method should return a class, interface, or record type. The class, interface, or record returned by GetEnumerator() must contain an open instance method named MoveNext() . The MoveNext() method should return a Boolean . - The class, interface, or record returned by GetEnumerator() must contain a public instance, a read-only property called Current . The type of the Current property must be the type contained in the collection.
How did the language developers intend to use enums in my code?
- What to declare paramater type
- How to check for a
GetEnumerator method? - How to call the
GetEnumerator method? - How can I name the
Current property? - How do I call the
Next method?
For example:
TContoso = class(TSkyrim) public procedure AddObjects(const Objects); end; procedure TContoso.AddObjects(const Objects); var o: TObject; enumerator: TObject; bRes: Boolean; begin //for o in Objects do // InternalAdd(nil, '', o); if not HasMethod(Objects, 'GetEnumerator') then Exit; enumerator := InvokeMethod(Objects, 'GetEnumerator'); if not HasMethod(enumerator, 'MoveNext') then Exit; bRes := InvokeMethod(enumerator, 'MoveNext'); while bRes do begin if HasMethod(enumerator, 'Current'); InternallAdd(nil, '', InvokeMethod(enumerator, 'Current')); bRes := InvokeMethod(enumerator, 'MoveNext'); end; end;
What is the intended way to convey an "enumerable bag of things"?
Hack
TContoso = class(TSkyrim) public procedure AddObjects(Objects: TList); overload; procedure AddObjects(Objects: TList<T>); overload; end;
There must be a reason designers decided not to have IList implement IEnumerable . To enumerate the list, a compilation mechanism must exist. But what is the reason, and what is it.