Working on some coding last night, and would like to point out and remind myself how to implement IEnumerable<T> properly.
When implementing IEnumerable<T>, there is always a need to use the method resolution clause in order to avoid conflict of the similar names in IEnumerable and IEnumerable<T>, since both declares the GetEnumerator method with the same type signature.
WORKING EXAMPLE: IEnumerable<T>
type TTestEnumerable<T> = class(TInterfacedObject, IEnumerable<T>) function GenericGetEnumerator: IEnumerator<T>; function GetEnumerator: IEnumerator; function IEnumerable<T>.GetEnumerator = GenericGetEnumerator; end;
Similarly, for IEnumerator<T>:
type TTestEnumerator<T> = class(TInterfacedObject, IEnumerator<T>) function GetCurrent: TObject; function MoveNext: Boolean; procedure Reset; function GenericGetCurrent: T; function IEnumerator<T>.GetCurrent = GenericGetCurrent; end;
The lessons learnt from the above is that whenever interfaces with generic ancestors are involved with similarly named methods, one needs to declare the ancestor interface method, and use method resolution clause to redirect the descendent methods.
While working on the above, I filed QC 116432, on how class completion doesn't work when generics are involved.
In 2017, with the release of Delphi 10.2 Tokyo, Embarcadero introduced a specialized implementation of the Observer pattern into the System.Classes unit. While it has been in the wild for 9 years, it remains a "hidden" architecture for many, primarily because it serves as the invisible engine behind LiveBindings. Other than live bindings, you can also use the Observer pattern as a way to update component settings to the Windows registry, an .ini file, or persist it elsewhere.
System.Classes