Store Generic Array in Class using Delphi XE - delphi

Using the structure below, how can I define my TAnimalCollection class in order to store my collection? Calling either SelectAll or SelectTop10 will update the SelectedRecords.
Removing the private field allows the code to compile, but there is no mechanism to then store the returned result set.
TDog = class
private
FBreed: string;
public
property Breed: string read FBreed write FBreed;
end;
TCat = class
private
IsWild: string;
public
property IsWild: string read FIsWild write FIsWild;
end;
TMyArray<T> = array of T;
TAnimalCollection = class
private
SelectedRecords: TMyArray<T>; // Generates: Undeclared Identifier: 'T'
public
function SelectAll<T>: TMyArray<T>;
function SelectTop10<T>: TMyArray<T>;
// Other Methods
end;

First, you don't need TMyArray; the built-in TArray type does the same thing.
The compiler is correct, though. In your field declaration, there's no such thing as T. The generic argument needs to be introduced on the left of a declaration before it can be used on the right. If Delphi supported generic fields, the declaration would look like this:
SelectedRecords<T>: TArray<T>;
But it doesn't, and you wouldn't want it to in this case anyway. You apparently want to store two completely unrelated classes together in the same array simultaneously. An array is always of a single type. The only single type that unifies TDog and TCat is TObject, so your array needs to be of that type:
SelectedRecords: TArray<TObject>;
// or, more conventionally,
SelectedRecords: array of TObject;
You're welcome to declare a "generic array," but only as a field of a generic class or a variable of a generic method. If you could declare a standalone generic array, try to think of when the actual type of the array elements would be determined. If not at the point you declare the array, then when? With classes and methods, you specify the type argument(s) when you declare a variable of the class, instantiate the class, or call the method. Those are uses that are separate from their declarations, and each use is distinct. When you declare a variable, you must use it the same way you declared it — a variable's type cannot change without recompiling the program.

Related

Why static is needed in the one case and is not mandatory in another one?

FreePascal, Delphi mode. After some experiments I found that this code is compiled, FPC says me that class procedure must be "static". But my question is: then why operator Equal does not need "static" and it's compiling fine?! Also I can not understand what is the difference to have "class" procedure and "class" with "static" (for example, in Python in classmethod you get argument - reference to the class, in staticmethod - you have not such argument).
type TPos = record
FLine: Word;
FPos: Word;
class procedure Init(out a: TPos); static;
class operator Equal(a, b: TPos): Boolean;
end;
PS. I set "delphi" tag because: 1) it's written in delphi mode 2) because I found the same documentation for Delphi: about class and static keywords.
Contrary to what you state, a non-static class method does have a reference to the class passed as an argument. It's an implicit argument named Self.
For class methods on records as opposed to classes, since there is no inheritance, this Self parameter would serve no purpose, and so it is never passed. Hence all class methods on records must be static.
A class operator is implicitly a static method and so you don't need to state that. In other words, operator implies static.

Delphi Use calling objects properties in added class method

I want to add a method to an existing Delphi class. I think basic class framework OK but need to access some properties of the object that called my method in my method. I can't seem to get anything to work.
old class TStringGrid
new class OptStringGrid where myNewMethod is referenced
//example of new class method
procedure myNewMethod (const Name: string);
begin
//Here is my question location.
// I would like to access properties of the calling object in this case
// testgrid. Like... i:= testgrid.rowcount;
end
// Unit Calling statements
var
testGrid : OptStringGrid;
i: integer;
begin
i := testgrid.myNewMethod(strName);
end;
New to Delphi, forgive my terminology if wrong please. I know example code not compilable. I'm looking for techniques to access the properties as described.
To access members of the object whose method is executing, you can use the Self variable. It's automatically declared and assigned inside any method body. In fact, its use is usually implicit — any members of the object are automatically in scope inside the method body. You generally only need to qualify member access with Self when there is already some other variable in the method that has the same name as the member you wish to use.
The key thing about implementing methods is that you need to make sure they're actually methods. The code shown in the question does not define myNewMethod as a method. Rather, it's a standalone subroutine. Only methods can be called on objects, and therefore only methods can have access to the objects they're called on.
A method declaration appears inside a class declaration. Yours might look something like this:
type
TOptStringGrid = class(TStringGrid)
public
function myNewMethod(const Name: string): Integer;
end;
A method definition appears in the implementation section of your unit, along with all your other subroutine bodies, just like all the event-handler implementations the IDE creates for you when you double-click events in the Object Inspector. Those are just ordinary methods.
What distinguishes a method implementation from an implementation of some other kind of subroutine is that the method name includes the name of the class it belongs to:
function TOptStringGrid.myNewMethod(const Name: string): Integer;
begin
// ...
end;
Observe the TOptStringGrid. portion in the code above. That's how the compiler knows that the method body belongs to that class and not anything else named myNewMethod.
Within that method body, you can access all the published, public, and protected members of the ancestor class, TStringGrid, including the RowCount property.

How do I declare a variable of a generic type without knowing the type arguments?

How do I define a generic TList type so that I can declare a variable of that type and then assign any specialization of TList<> to it?
I want to declare this variable:
var
MyList:THowToDeclareThisListType<T>;
And then instantiate it like this:
MyList:=THowToDeclareThisListType<integer>.Create;
or
MyList:=THowToDeclareThisListType<double>.Create;
etc. I must be missing something pretty obvious here. I don't want classes, just a simple type definition.
You are trying to declare a variable like this:
var
List: TList<?>;
such that List can be assigned objects of type TList<Integer> or TList<Double> or TList<string>.
That is not possible. When you define a variable using a generic type, the type must be fully instantiated.
The only way that you can have a variable that holds any object of type TList<T> is if the variable is declared to have a common base class to TList<T>. And the common base class cannot be a non-instantiated generic. For TList<T> the only possible common base class is TObject.
So you could write
var
List: TObject;
and then assign any of your objects to List. But I'm not sure that would be terribly useful!

Property using Generics in Delphi

I'm trying to write a property which uses generics:
type TMyClass = class
protected
function GetCountBy<T: Class>: Integer;
public
property CountBy<T: Class>: Integer read GetCountBy<T>;
end;
but the compile fails on the property declaration with the message 'Property CountBy does not exist in base class', and the red squiggle on the opening < of the property name.
Is there any way to achieve this?
Edit:
Here's my other use case, which is more complex but more real world:
property ItemsBy<T: Class>[Index: Integer]: T read GetItemsBy<T> write SetItemsBy<T>;
The function filters the contents of a list to return the Index'th item of the specified class.
Generic properties are not supported in Delphi. Only generic classes, or generic methods.
I can't find anything in the documentation that explicitly states that limitation. On the other hand the documentation only describes generic classes and generic methods. And the new language grammar to support generics also makes no mention of properties.
I'm not up to speed on generics but shouldn't the declaration be more like this
type TMyClass<T: class> = class
protected
function GetCountBy<T>: Integer;
public
property CountBy<T>: Integer read GetCountBy<T>;
end;

Delphi, is possible have a property and function with the same name?

consider this code
type
TMyObject=class
private
FName: Integer;
function Name: Integer;
public
property Name : Integer read FName;
function Name(Param1 : integer) : Integer; overload;
end;
Is possible in delphi create a property and a function (or procedure) with the same name?
Exist any directive or compiler switch which allow create a class like that?
No, it is not. In addition, there is no directive or compiler switch that would allow this.
The answer is in the error message that you got when you tried this...you did try it right?
Since i see the overload keyword. I suspect that perhaps what you need default are parameters
If you use
function Name(Param1: Integer = SOME_VALUE): Integer;
it can be called as either :=Name or :=Name(5)
By accident I discovered it is possible to have a class with a property that has the same name as a function in the parent class (or vice versa).
However, I would avoid this because it will confuse you. Especially if the function and property have different meanings!
Currently, the compiler cannot do what you want.
In theory, a future version of the compiler could:
The signature of a method (overloaded or not) consists of the name and the parameter types.
The same holds for the signature of indexed properties.
Since the signature spaces of properties and methods are partially linked (hence the compiler error message), that combined space could be extended to include property overloads.
Of course that extension can backfire because of backward compatibility.
--jeroen

Resources