RDP1974 40 Posted October 25, 2022 can you suggest me a good tutorial about Delphi generics? especially, when and where they are useful? in which purposes? performance wyse are they trustable? Share this post Link to post
Stefan Glienke 2002 Posted October 25, 2022 (edited) As a reaction to one of his answers during Q&A I wrote a blog post. Having said that and personally loving generics for various use cases (as shown in the blog post) there are also things that are solved sub optimal - which I also wrote about. Also if you have used generics in C# then you will likely miss co- and contravariance - oh, look, I also wrote about that. If you are going really fancy with generics and code that uses RTTI you have to be aware about some percularities - guess what: wrote about it. Now because in generics you basically have the lowest common denominator and we are lacking quite some ways to specify some traits of the supported types via constraints there are several things that you cannot do or have to fall back to indirections: most common example is using comparer interfaces for generic sorting algorithm or hashtables. That is mostly where a naively implemented generic algorithm might be slower than some handcrafted code for the specific type unless you heavily optimize for various cases (as I have done in Spring). Edited October 25, 2022 by Stefan Glienke 6 Share this post Link to post
Remy Lebeau 1394 Posted October 25, 2022 51 minutes ago, RDP1974 said: can you suggest me a good tutorial about Delphi generics? Have you read the official documentation yet? https://docwiki.embarcadero.com/RADStudio/en/Generics_Index 1 Share this post Link to post
Fr0sT.Brutal 900 Posted October 26, 2022 (edited) 14 hours ago, RDP1974 said: when and where they are useful? in which purposes? It's mostly about containers. You can have TList containing any type without ugly casting to/from Pointer. Edited October 26, 2022 by Fr0sT.Brutal Share this post Link to post
David Heffernan 2345 Posted October 26, 2022 4 hours ago, Fr0sT.Brutal said: It's mostly about containers. Apart from the myriad of other applications that aren't about containers 3 Share this post Link to post
Fr0sT.Brutal 900 Posted October 26, 2022 54 minutes ago, David Heffernan said: Apart from the myriad of other applications that aren't about containers Name at least 10 of that myriad? I personally meet them in: - containers - my syntax-sugaring enum/set wrapper - Virtualtreeview's GetNodeData - might be considered container stuff as well I also saw template-alike application (TProc2 = procedure <T1,T2>) but it's hardly a key feature IMHO Share this post Link to post
Vincent Parrett 750 Posted October 27, 2022 I use generics a lot in non container/collection scenarios - for example Delphi Mocks fluent api uses the generic type to allow a type safe definition of the mock. Without generics we would be using strings - which is not typesafe and would not survive refactoring. Another example - I have lexer/parser library (used in FinalBuilder) //NOTE : T MUST be an Enumerated Type (need better constraints!) TTokenRec<T> = record private ..... end; ILexer<T> = interface function Next : TTokenRec<T>; ... TBaseLexer<T> = class(TInterfacedObject,ILexer<T>) //concrete usages TDSLLexer = class(TBaseLexer<TDSLTokenKind>,IDSLLexer) TVariableSenseLexer = class(TBaseLexer<TTokenKind>) //parsers built on top of the lexers IParser<TAstNodeType,TParseErrorType> = interface TBaseParser<TTokenType,TAstNodeType,TParseErrorType> = class(TInterfacedObject,IParser<TAstNodeType,TParseErrorType>) TDSLParser = class(TBaseParser<TDSLTokenKind,TDSLASTNodeType,TDSLParserErrorType>) TVariableSenseParser = class (TBaseParser<TTokenKind, TVariableSenseASTNodeType, TVariableSenseParserErrorType>) Generics allows you to avoid copying and pasting tons of boilerplate code, changing types etc, or doing having to do tons of nasty type casting. If Delphi's generics were better there would be many more uses for them, but when you attempt anything complex you run into limitations. If you really want to know what else can be done with generics, you would have to look at other languages that have better generics implementations (like c#). 4 Share this post Link to post
Stefan Glienke 2002 Posted October 27, 2022 Think of collections as "algorithms and datatypes for any type" - then you know the use case of generics. For any algorithm and/or datatype that is not just specific for one exact type. Share this post Link to post
David Heffernan 2345 Posted October 27, 2022 (edited) 21 hours ago, Fr0sT.Brutal said: Name at least 10 of that myriad? I personally meet them in: - containers - my syntax-sugaring enum/set wrapper - Virtualtreeview's GetNodeData - might be considered container stuff as well I also saw template-alike application (TProc2 = procedure <T1,T2>) but it's hardly a key feature IMHO From my code base I find this: TThreadsafe<T> = class private FLock: TCriticalSection; FValue: T; function GetValue: T; procedure SetValue(const NewValue: T); public constructor Create; destructor Destroy; override; property Value: T read GetValue write SetValue; end; And this: Enum<T: record> = class strict private class function TypeInfo: PTypeInfo; static; class function TypeData: PTypeData; static; public class function IsEnum: Boolean; static; class function Count: Integer; static; class function ToOrdinal(Value: T): Integer; static; class function FromOrdinal(Value: Integer): T; static; class function TryFromOrdinal(Value: Integer; out Enum: T): Boolean; static; class function ToString(Value: T): string; static; class function FromString(const Value: string): T; static; class function MinValue: Integer; static; class function MaxValue: Integer; static; class function InRange(Value: Integer): Boolean; static; class function EnsureRange(Value: Integer): Integer; static; class function FromName(const Name: string): T; static; class function TryFromName(const Name: string; out Enum: T): Boolean; static; class function ToName(Item: T): string; static; class function ToNewSentenceName(Item: T): string; static; class function ToMidSentenceName(Item: T): string; static; class function ToUpperCaseName(Item: T): string; static; class function ToPascalCaseName(Item: T): string; static; end; And this: TArray = class public type TWriteItem<T> = reference to procedure(const TaggedFile: ITaggedFile; const Item: T); TReadItem<T> = reference to procedure(const TaggedFile: ITaggedFile; out Item: T); strict private const SortOrderFactor: array [TSortOrder] of Integer = (1, -1); public class procedure Swap<T>(var Left, Right: T); static; class procedure Reverse<T>(var Values: array of T; Index, Count: Integer); overload; static; class procedure Reverse<T>(var Values: array of T); overload; static; class function Reversed<T>(const Values: array of T): TArray<T>; static; class procedure Shuffle<T>(var Values: array of T; const Random: TFunc<Integer, Integer>; Index, Count: Integer); overload; static; class procedure Shuffle<T>(var Values: array of T; const Random: TFunc<Integer, Integer>); overload; static; class function Contains<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index, Count: Integer; out ItemIndex: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; out ItemIndex: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index, Count: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; EqualOp: TEqualOp<T>; Index, Count: Integer; out ItemIndex: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; EqualOp: TEqualOp<T>; out ItemIndex: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; EqualOp: TEqualOp<T>; Index, Count: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; EqualOp: TEqualOp<T>): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; Index, Count: Integer; out ItemIndex: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; out ItemIndex: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T; Index, Count: Integer): Boolean; overload; static; class function Contains<T>(const Values: array of T; const Item: T): Boolean; overload; static; class function Matches<T>(const Values: array of T; const Predicate: TPredicate<T>; out ItemIndex: Integer): Boolean; overload; static; class function Matches<T>(const Values: array of T; const Predicate: TPredicate<T>): Boolean; overload; static; class function Matches<T>(const Values: array of T; const Predicate: TPredicateOfObj<T>; out ItemIndex: Integer): Boolean; overload; static; class function Matches<T>(const Values: array of T; const Predicate: TPredicateOfObj<T>): Boolean; overload; static; class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>; Index, Count: Integer): Integer; overload; static; class function IndexOf<T>(const Values: array of T; const Item: T; const Comparer: IEqualityComparer<T>): Integer; overload; static; class function IndexOf<T>(const Values: array of T; const Item: T; EqualOp: TEqualOp<T>; Index, Count: Integer): Integer; overload; static; class function IndexOf<T>(const Values: array of T; const Item: T; EqualOp: TEqualOp<T>): Integer; overload; static; class function IndexOf<T>(const Values: array of T; const Item: T; Index, Count: Integer): Integer; overload; static; class function IndexOf<T>(const Values: array of T; const Item: T): Integer; overload; static; class function Equal<T>(const lhs, rhs: array of T; const Comparer: IEqualityComparer<T>): Boolean; overload; static; class function Equal<T>(const lhs, rhs: array of T; EqualOp: TEqualOp<T>): Boolean; overload; static; class function Equal<T>(const lhs, rhs: array of T): Boolean; overload; static; class function Ordered<T>(const Values: array of T; const Comparer: IComparer<T>; Index, Count: Integer): Boolean; overload; static; class function Ordered<T>(const Values: array of T; const Comparer: IComparer<T>): Boolean; overload; static; class function Ordered<T>(const Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer): Boolean; overload; static; class function Ordered<T>(const Values: array of T; const Comparison: TComparison<T>): Boolean; overload; static; class function Ordered<T>(const Values: array of T; Index, Count: Integer): Boolean; overload; static; class function Ordered<T>(const Values: array of T): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; const Comparer: IEqualityComparer<T>; out Value: T; Index, Count: Integer): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; const Comparer: IEqualityComparer<T>; out Value: T): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; EqualOp: TEqualOp<T>; out Value: T; Index, Count: Integer): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; EqualOp: TEqualOp<T>; out Value: T): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; out Value: T; Index, Count: Integer): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; out Value: T): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; const Comparer: IComparer<T>; out Value: T; Index, Count: Integer): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; const Comparer: IComparer<T>; out Value: T): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; const Comparison: TComparison<T>; out Value: T; Index, Count: Integer): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; const Comparison: TComparison<T>; out Value: T): Boolean; overload; static; class function HasDuplicates<T>(const Values: array of T; const Comparison: TComparison<T>): Boolean; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparer: IEqualityComparer<T>; Index, Count: Integer; const Dispose: TProcOfObj<T>): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparer: IEqualityComparer<T>; Index, Count: Integer): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparer: IEqualityComparer<T>): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; EqualOp: TEqualOp<T>; Index, Count: Integer; const Dispose: TProcOfObj<T>): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; EqualOp: TEqualOp<T>; Index, Count: Integer): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; EqualOp: TEqualOp<T>): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparer: IComparer<T>; Index, Count: Integer; const Dispose: TProcOfObj<T>): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparer: IComparer<T>; Index, Count: Integer): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparer: IComparer<T>): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; const Comparison: TComparison<T>): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T; Index, Count: Integer): Integer; overload; static; class function RemoveDuplicates<T>(var Values: array of T): Integer; overload; static; class function Sorted<T>(const Values: array of T; const Comparer: IComparer<T>; Order: TSortOrder; Index, Count: Integer): Boolean; overload; static; class function Sorted<T>(const Values: array of T; const Comparer: IComparer<T>; Index, Count: Integer): Boolean; overload; static; class function Sorted<T>(const Values: array of T; const Comparer: IComparer<T>; Order: TSortOrder): Boolean; overload; static; class function Sorted<T>(const Values: array of T; const Comparer: IComparer<T>): Boolean; overload; static; class function Sorted<T>(const Values: array of T; const Comparison: TComparison<T>; Order: TSortOrder; Index, Count: Integer): Boolean; overload; static; class function Sorted<T>(const Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer): Boolean; overload; static; class function Sorted<T>(const Values: array of T; const Comparison: TComparison<T>; Order: TSortOrder): Boolean; overload; static; class function Sorted<T>(const Values: array of T; const Comparison: TComparison<T>): Boolean; overload; static; class function Sorted<T>(const Values: array of T; Order: TSortOrder; Index, Count: Integer): Boolean; overload; static; class function Sorted<T>(const Values: array of T; Index, Count: Integer): Boolean; overload; static; class function Sorted<T>(const Values: array of T; Order: TSortOrder): Boolean; overload; static; class function Sorted<T>(const Values: array of T): Boolean; overload; static; class procedure Sort<T>(var Values: array of T; const Comparer: IComparer<T>; Order: TSortOrder; Index, Count: Integer); overload; static; class procedure Sort<T>(var Values: array of T; const Comparer: IComparer<T>; Index, Count: Integer); overload; static; class procedure Sort<T>(var Values: array of T; const Comparer: IComparer<T>; Order: TSortOrder); overload; static; class procedure Sort<T>(var Values: array of T; const Comparer: IComparer<T>); overload; static; class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>; Order: TSortOrder; Index, Count: Integer); overload; static; class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>; Index, Count: Integer); overload; static; class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>; Order: TSortOrder); overload; static; class procedure Sort<T>(var Values: array of T; const Comparison: TComparison<T>); overload; static; class procedure Sort<T>(var Values: array of T; Order: TSortOrder; Index, Count: Integer); overload; static; class procedure Sort<T>(var Values: array of T; Index, Count: Integer); overload; static; class procedure Sort<T>(var Values: array of T; Order: TSortOrder); overload; static; class procedure Sort<T>(var Values: array of T); overload; static; class function Copy<T>(const Source: array of T; Index, Count: Integer): TArray<T>; overload; static; class function Copy<T>(const Source: array of T): TArray<T>; overload; static; class procedure Move<T>(const Source: array of T; var Dest: array of T; Index, Count: Integer); overload; static; class procedure Move<T>(const Source: array of T; var Dest: array of T); overload; static; class function Concatenated<T>(const Source1, Source2: array of T): TArray<T>; overload; static; class function Concatenated<T>(const Source: array of TArray<T>): TArray<T>; overload; static; class procedure Initialise<T>(var Values: array of T; const Value: T); static; class function New<T>(Count: Integer; const Value: T): TArray<T>; static; class function Zero<T>(Count: Integer): TArray<T>; static; class procedure Zeroise<T>(var Values: array of T); static; class procedure Free<T: class>(const Values: array of T); static; class procedure FreeAndNil<T: class>(var Values: array of T); static; class function GetHashCode<T>(const Values: array of T; const Comparer: IEqualityComparer<T>): Integer; overload; static; class function GetHashCode<T>(const Values: array of T): Integer; overload; static; class function GetHashCode<T>(Values: Pointer; Count: Integer; const Comparer: IEqualityComparer<T>): Integer; overload; static; class function GetHashCode<T>(Values: Pointer; Count: Integer): Integer; overload; static; class procedure Write<T>(const TaggedFile: ITaggedFile; const WriteItem: TWriteItem<T>; const Values: TArray<T>); overload; static; class procedure Write<T>(const TaggedFile: ITaggedFile; const Values: TArray<T>); overload; static; class procedure Read<T>(const TaggedFile: ITaggedFile; const ReadItem: TReadItem<T>; out Values: TArray<T>); overload; static; class procedure Read<T>(const TaggedFile: ITaggedFile; out Values: TArray<T>); overload; static; class procedure Skip(const TaggedFile: ITaggedFile); static; end; And this: NDArray<T> = record public type {$POINTERMATH ON} P = ^T; {$POINTERMATH OFF} private type TEnumerator = record private FOwner: ^NDArray<T>; FCurrentIndices: TArray<Integer>; function GetCurrent: T; public class function New(const Owner: NDArray<T>): TEnumerator; static; property Current: T read GetCurrent; function MoveNext: Boolean; end; private FBase: P; FValues: TArray<T>; FRank: Integer; FShape: TArray<Integer>; FStride: TArray<NativeInt>; function LinearIndex(const Indices: array of Integer): NativeInt; function GetCount: NativeInt; function GetItem(const Indices: array of Integer): T; procedure SetItem(const Indices: array of Integer; const Value: T); function GetSlice(const Indices: array of Integer): NDArray<T>; procedure SetSlice(const Indices: array of Integer; const Value: NDArray<T>); function GetIsEmpty: Boolean; function GetHasZeroDimension: Boolean; public class function New(const Shape: array of Integer): NDArray<T>; overload; static; class function New(const Shape: array of Integer; const Values: TArray<T>): NDArray<T>; overload; static; class function New(const Shape: array of Integer; const Value: T): NDArray<T>; overload; static; class function Zero(const Shape: array of Integer): NDArray<T>; static; class function Diagonal(const Values: array of T): NDArray<T>; overload; static; class function Diagonal(Size: Integer; const Value: T): NDArray<T>; overload; static; class function Empty: NDArray<T>; static; class operator Explicit(const arr: TArray<T>): NDArray<T>; property IsEmpty: Boolean read GetIsEmpty; property HasZeroDimension: Boolean read GetHasZeroDimension; property Rank: Integer read FRank; property Shape: TArray<Integer> read FShape; property Stride: TArray<NativeInt> read FStride; property Count: NativeInt read GetCount; property DataPtr: P read FBase; property Items[const Indices: array of Integer]: T read GetItem write SetItem; default; function ItemPtr(const Indices: array of Integer): P; property Slice[const Indices: array of Integer]: NDArray<T> read GetSlice write SetSlice; procedure CopyTo(Dest: P; Count: NativeInt); function Clone: NDArray<T>; function ToFlattenedArray: TArray<T>; class procedure Write(const TaggedFile: ITaggedFile; const WriteItem: TArray.TWriteItem<T>; const Value: NDArray<T>); overload; static; class procedure Write(const TaggedFile: ITaggedFile; const Value: NDArray<T>); overload; static; class procedure Read(const TaggedFile: ITaggedFile; const ReadItem: TArray.TReadItem<T>; out Value: NDArray<T>); overload; static; class procedure Read(const TaggedFile: ITaggedFile; out Value: NDArray<T>); overload; static; public function GetEnumerator: TEnumerator; end; And this: TIterativeSolver<T> = class public type P = ^T; protected class procedure AGSIterate(A, b, x, work: P; N: Integer; Storage: TMatrixStorage); static; class procedure AGSCalcResidual(A, b, x, r: P; N: Integer; Storage: TMatrixStorage); static; class function AGSIsZero(b: P; N: Integer): Boolean; static; class function AGSSqrMag(x: P; N: Integer): Double; static; class function AGSAccel(r, x: P; work: Pointer; iter, N: Integer): Boolean; static; public class function SolveAGS(const A: NDArray<T>; var b: NDArray<T>; maxiter: Integer; tol: Double; out iter: Integer): Boolean; static; end; And this: type TLUSolver<T> = class public type P = ^T; TWorkspace = record public ipiv: TArray<Integer>; public procedure Update(N: Integer); end; protected //protected to avoid spurious compiler warning class procedure DecomposeDense(A: P; ipiv: PInteger; N: Integer); overload; static; class procedure SubstituteDense(A: P; ipiv: PInteger; N: Integer; b: P; Storage: TMatrixStorage); static; class procedure DecomposeBanded(A: P; ipiv: PInteger; N, Bandwidth, ldab: Integer); overload; static; class procedure SubstituteBanded(A: P; ipiv: PInteger; N, Bandwidth, ldab: Integer; b: P; Storage: TMatrixStorage); static; class procedure DecomposeDense(A: P; N: Integer; var Workspace: TWorkspace); overload; static; class procedure DecomposeBanded(A: P; N, Bandwidth, ldab: Integer; var Workspace: TWorkspace); overload; static; public class procedure Decompose(var A: NDArray<T>; var Workspace: TWorkspace); static; class procedure Substitute(var A, b: NDArray<T>; const Workspace: TWorkspace); static; class procedure Solve(var A, b: NDArray<T>); static; end; And this: TData<T> = record x: T; xPrime: TArray<T>; xPrimePrime: TArray<TArray<T>>; xPrimePrimePrime: TArray<TArray<TArray<T>>>; end; And this: TTableItem<T: TModelObject> = record public ModelObject: T; ObjectIndex: Integer; ItemIndex: Integer; public class function New(ModelObject: T; ObjectIndex, ItemIndex: Integer): TTableItem<T>; static; class function DisplayName(ModelObject: TModelObject; const ItemName: string; ItemIndex: Integer): string; overload; static; function DisplayName(const ItemName: string): string; overload; procedure RedirectDataAccess(var Handler: TDataObject; var Index: Integer); end; I could go on, but it's pretty boring. And these are just some examples from one particular code base. Other code bases will have different use cases. As Stefan said, generics are used for generic programming. Quote Generic programming is a style of computer programming in which algorithms are written in terms of types to-be-specified-later that are then instantiated when needed for specific types provided as parameters. This approach, pioneered by the ML programming language in 1973,[1][2] permits writing common functions or types that differ only in the set of types on which they operate when used, thus reducing duplication. Such software entities are known as generics in Ada, C#, Delphi, Eiffel, F#, Java, Nim, Python, Go, Rust, Swift, TypeScript and Visual Basic .NET. They are known as parametric polymorphism in ML, Scala, Julia, and Haskell (the Haskell community also uses the term "generic" for a related but somewhat different concept); templates in C++ and D; and parameterized types in the influential 1994 book Design Patterns.[3] So yeah, a myriad. Edited October 27, 2022 by David Heffernan 1 Share this post Link to post
Fr0sT.Brutal 900 Posted October 27, 2022 32 minutes ago, David Heffernan said: From my code base I find this: Well, 3 samples from these are containers and 1 is enum wrapper 😛 But OK, I got your point. Just haven't used generics massively myself Share this post Link to post
RDP1974 40 Posted October 27, 2022 Delphi language is evolved a lot in the last years should be very useful a blog where to show the new language capabilities btw. example public type TWriteItem<T> = reference to procedure(const TaggedFile: ITaggedFile; const Item: T); a type inside a class? what is a reference to procedure? 2 hours ago, Stefan Glienke said: Think of collections as "algorithms and datatypes for any type" - then you know the use case of generics. For any algorithm and/or datatype that is not just specific for one exact type. great, under the hood the enumeration how it is engineered? hash table? binary-search-tree? so the compiler will produce "bloat" code for every generic class if I see, else how to solve the fact that the type it is not defined before??? Share this post Link to post
Sherlock 663 Posted October 27, 2022 10 minutes ago, RDP1974 said: Delphi language is evolved a lot in the last years should be very useful a blog where to show the new language capabilities Not a blog, but even better (IMHO): https://stackoverflow.com/questions/8460037/list-of-delphi-language-features-and-version-in-which-they-were-introduced-depre 1 Share this post Link to post
David Heffernan 2345 Posted October 27, 2022 58 minutes ago, Fr0sT.Brutal said: 3 samples from these are containers I don't really see anything that is what I would describe as a container / collection. Share this post Link to post
David Heffernan 2345 Posted October 27, 2022 21 minutes ago, RDP1974 said: a type inside a class? Yes, nested types. Really just a type declared in the lexical scope of the containing type. 22 minutes ago, RDP1974 said: what is a reference to procedure? Delphi syntax for anonymous methods. Share this post Link to post
Fr0sT.Brutal 900 Posted October 27, 2022 3 hours ago, David Heffernan said: I don't really see anything that is what I would describe as a container / collection. Isn't array a container? Share this post Link to post
David Heffernan 2345 Posted October 27, 2022 4 minutes ago, Fr0sT.Brutal said: Isn't array a container? An array is a container, but that TArray isn't an array, it's a class containing a lot of static methods. Share this post Link to post
Stefan Glienke 2002 Posted October 27, 2022 4 minutes ago, David Heffernan said: it's a class containing Doesn't that make it a container? 😜 10 Share this post Link to post
David Heffernan 2345 Posted October 27, 2022 Just now, Stefan Glienke said: Doesn't that make it a container? Stop trolling, that's my job! 10 Share this post Link to post
skyzoframe[hun] 4 Posted November 2, 2022 (edited) Hi, I like to use it in this way.: (here you will find example files) I think the best book for generics.: Pawel Glowacki (RIP) - Expert Delphi How to separate units, back and front end, types etc.. Edited November 2, 2022 by skyzoframe[hun] Share this post Link to post