How to give setter a 2nd parameter in Delphi? - delphi

I want to know whether we can do such in Delphi:
I have a private procedure:
procedure SetMySend(const oValue: TTM_MySend_Profile;
displayValue: string = '...');
I have a public property:
property MySend: TTM_MySend_Profile displayLocateID '...'
read FMySend write SetMySend;
Can I give a parameter displayValue here as the 2nd parameter of the setter? I cannot get this compiled.
I cannot figure out the correct way to do it and wonder whether I can do this in Delphi. Thanks for help!

A property setter for a property takes only one parameter, of the same type as the property. There is no syntax that would allow you to write the type of code you are attempting to write. Note that I am ignoring array properties which are not pertinent here.
What you need to do is to write a dedicated setter which supplies the extra parameter to your SetMySend function.
procedure SetMySend(const Value: TTM_MySend_Profile;
const displayValue: string); overload;
procedure SetMySend(const Value: TTM_MySend_Profile); overload;
property MySend: TTM_MySend_Profile read FMySend write SetMySend;
And then in the implementation you write
procedure TMyClass.SetMySend(const Value: TTM_MySend_Profile);
begin
SetMySend(Value, '...');
end;
You could hijack index specifiers to effect something similar, but I would not recommend that.

Related

No overloaded version with these arguments - var param with read/write property

I have two overloaded procedures, of which I want to call the second one:
function ModifySyncProperties(AEWSItemId: String; AEvent: TcxSchedulerEvent; var AEWSChangeKey: String): Boolean; overload;
function ModifySyncProperties(AEWSItemId: String; ATTID: Integer; ASyncID: String; var AEWSChangeKey: String): Boolean; overload;
This fails with an error, though:
lSuccess := FDMExchange.ModifySyncProperties(lEWSId, lApp.EventID, lNewOutlookID, lApp.EWSItemChangeKey);
There is no overloaded version of 'ModifySyncProperties' that can be called with these arguments
This works, though:
lChangeKey := lApp.EWSItemChangeKey;
lSuccess := FDMExchange.ModifySyncProperties(lEWSId, lApp.EventID, lNewOutlookID, lChangeKey);
lApp.EWSItemChangeKey := lChangeKey;
Here are the types and variables:
lNewOutlookID,
lEWSID,
lChangeKey : String;
lApp : TEWSAppointment;
lSuccess : Boolean;
TEWSAppointment is defined in the interface section of another unit as this:
TEWSAppointment = class
private
FEventID: Integer;
...
FEWSItemChangeKey: String;
...
public
property EventID: Integer read FEventID write FEventID;
...
property EWSItemChangeKey: String read FEWSItemChangeKey write FEWSItemChangeKey;
...
end;
Why does the compiler not accept the read/write lApp property as a var parameter?
I'm using Delphi Rio 10.3.1.
The documentation for var parameters says:
If a routine's declaration specifies a var parameter, you must pass an assignable expression - that is, a variable, typed constant (in the {$J+} state), dereferenced pointer, field, or indexed variable to the routine when you call it.
A property does not meet this requirement.
Further, the documentation for properties calls this out explicitly:
Unlike fields, properties cannot be passed as var parameters, nor can the # operator be applied to a property.
At the implementation level, the ABI, var parameters are implemented by passed the address of the variable. Since a property does not necessarily have a backing variable with an address, the compiler cannot directly obtain such a variable address.
Furthermore, if the property getter or setter perform actions beyond reading or writing to a variable, then they would need to be called.
In principle at least the language could support the usage you wish by declaring a local variable and compiling this code:
localVar := myProperty;
foo(localVar);
myProperty := localVar;
However, the designers of the compiler did not implement this when properties were introduced to the language.

Can a property setter use open array parameter?

The doc says dynamic arrays can be passed into a function/procedure using open array parameters.
For example, the following works, in accordance with the doc.
type
TAInteger = array of Integer;
function Work(const A: array of Integer): Integer;
begin
Result := Length(A);
end;
However, the code below complains about E2008 Incompatible types:
type
TTest = class
private
procedure SetIntegerArray(const Value: array of Integer);
published
property Value: TAInteger write SetIntegerArray;
end;
I am confused why there is the "Incompatible types" error, and whether a property setter can use open array parameters ?
Can a property setter use open array parameter?
No.
The type of the parameter that contains the new property value must be compatible with the property's type. An open array is not a type. Your property setter must be written like this:
procedure SetIntegerArray(const Value: TAInteger);

How to auto-generate getter and setter methods

I'm a Java developer and I always use the getter-setter methods. How can I use this concept in Delphi?
I define a local variable //1
I create a property //2
I press CTRL+SHIFT+C and the editor creates the getter and setter methods //3
for this example:
unit Unit1;
type
ClassePippo=class
private
colorText:string; //1
function getColorText: String; //3
procedure setColorText(const Value: String); //3
public
property colore: String read getColorText write setColorText; //2
end;
implementation
{ ClassePippo }
function ClassePippo.getColorText: String; //3
begin
Result:=colorText;
end;
procedure ClassePippo.setColorText(const Value: String); //3
begin
colorText:=Value;
end;
end.
Is there a feature to auto-create the getter and setter methods?
I only want to write colorText: string; //1 and invoke a shortcut and I want that the IDE auto-creates //2 and //3.
(When I develop in Java using Eclipse I can auto-generate the getter and setter methods using Source-->Generate getter and setter...)
Type out the property you want first rather than the internal variable. Just create type the following in your class So
Property Colore : String Read GetColorText Write SetColorText;
then press Ctrl Shift C
the IDE will then create the getter, the setter and the private internal variable.
Note that Property setters and getters are optional in Object Pascal. You can just as easily write
Property Colore : String Read FColorText Write FColorText;
or have just a setter or getter
Property Colore : String Read FColorText Write SetColorText;
In this case the IDE will generate the private FColorText variable and a setter method SetColorText
There is no feature in the IDE which will do what you want.
You can use Ctrl + Shift + C to generate getters and setters from a property declaration. But those getters and setters are empty stubs.
Frankly that is quite reasonable behaviour in my view. How can the IDE be expected to know how you wish to implement your getter and setter. It cannot be expected to know which field you intend to use. Your code is a good demonstration of why that is so. There is no obvious algorithmic relationship between the property name and the field name.
Another point to make is that if you want the IDE to generate the code for getter and setter in your question automatically, why do you even bother with a getter and setter? You could perfectly well write:
property colore: string read ColorText write ColorText;
If you wish to be able to use a feature as you describe you will need to find an extension to Delphi that implements the feature. The obvious candidates are CnPack and GExperts. Or if you cannot find such an extension, write one yourself.
You can do this in three steps.
Define the property with a getter and a variable to hold the value:
property OnOff: Boolean Read GetOnOff Write fOnOff;
Press Control + Shift + C to generate the variable and getter sub:
private
fOnOff: Boolean;
function GetOnOff: Boolean;
...
And
function TMyClass.GetOnOff: Boolean;
begin
Result := fOnOff;
end;
Now change the property to read the variable and write with a setter:
property OnOff: Boolean Read fOnOff Write SetOnOff;
Press Control + Shift + C again to generate the setter sub:
procedure TMyClass.SetOnOff(const Value: Boolean);
begin
fOnOff := Value;
end;
Now change the property to use the getter and setter:
property OnOff: Boolean Read GetOnOff Write SetOnOff;
Yes, it's a bit convoluted but if you have a lot of properties to define at once it can help.

Variable Required error with TStream.Write()

I am trying to compile a project but I guess this error "Variable Required"
function ReadInteger(SomeTStream:TStream):integer;
begin
SomeTStream.Read(Result, SizeOf(Result));
end;
Top:=ReadInteger(SomeTStream);
Left:=ReadInteger(SomeTStream);
Height:=ReadInteger(SomeTStream);
Width:=ReadInteger(SomeTStream);
Then when it tries to write it stops in Top and Left.
SomeTStream.Write(Top,SizeOf(Top));
^
SomeTStream.Write(Left,SizeOf(Top));}
^
E2036 Variable required
I read about the problem in here
But still I have no idea what should I do to fix it.
It's exactly as the error says. The parameter must be a variable. But you are passing a property that is implemented using a getter function. Copy the property into a variable and pass that to the function.
var
Value: Integer;
....
Value := Top;
Stream.Write(Value, SizeOf(Value));
Some helper methods would be most useful here to avoid drowning in boiler-plate. In fact you already added one for ReadInteger and you just need a matching one for WriteInteger.
procedure WriteInteger(Stream: TStream; Value: Integer);
begin
Stream.Write(Value, SizeOf(Value));
end;
What's going on here is that the Write method of TStream is declared like so:
function Write(const Buffer; Count: Longint): Longint; virtual; abstract;
Because this function receives an untyped parameter, it cannot be passed by value. The address of the parameter is actually passed, and so the caller needs to supply something with an address. Your property does not fit the bill.
Incidentally, usually, Write and Read are the wrong methods to call on TStream. They do not perform any error checking. They leave that to you. The Write and Read are abstract methods that are used by specialized classes to provide the stream implementation. You are generally expected to call WriteBuffer and ReadBuffer.
Or perhaps a binary writer would help.
var
bw: TBinaryWriter;
....
bw := TBinaryWriter.Create(Stream);
try
bw.Write(Top);
bw.Write(Left);
// etc.
finally
bw.Free;
end;

Untyped / typeless parameters in Delphi

What type are parameters without type like in the class TStringStream:
function Read(var Buffer; Count: Longint): Longint; override;
What is the type of Buffer parameter (is it a type of Pointer ?).
I wrote an article about this very topic a few years ago:
What is an untyped parameter?
Untyped parameters are used in a few situations; the TStream.Read method you ask about most closely matches with the Move procedure I wrote about. Here's an excerpt:
procedure Move(const Source; var Dest; Count: Integer);
The Move procedure copies data from an arbitrary variable
into any other variable. It needs to accept sources and destinations of
all types, which means it cannot require any single type. The procedure
does not modify the value of the variable passed for Source, so that
parameter’s declaration uses const instead of var, which is the
more common modifier for untyped parameters.
In the case of TStream.Read, the source is the stream's contents, so you don't pass that in as a parameter, but the destination is the Buffer parameter shown in the question. You can pass any variable type you want for that parameter, but that means you need to be careful. It's your job, not the compiler's, to ensure that the contents of the stream really are a valid value for the type of parameter you provide.
Read the rest of my article for more situations where Delphi uses untyped parameters.
Check out the Delphi help for "Untyped parameters"
You can pass in any type, but you have to cast it in the implementation. Help says that you cannot pass it a numeral or untyped numeric constant. So basically you have to be know what type to expect, and compiler can not help you, so you need a good reason to do it this way. I suppose it could be of use if you need the method to handle incompatible types, but then again you could write several overloaded versions for each expected type, I would suggest that as a better solution.
Perhaps surprisingly, it is legal to pass a dereferenced pointer as an untyped parameter. And the pointer itself doesn't even have to have a type.
procedure SomeMethod(var aParameter);
∶
procedure CallSomeMethod(aIsInteger : Boolean);
type
buffer : Pointer;
intValue : Integer;
realValue : Single;
begin
if aIsInteger then
begin
buffer := #intValue;
end
else
begin
buffer := #realValue;
end;
SomeMethod(buffer^);
Of course it would probably have been easier if the parameter to SomeMethod() had been a pointer, but this might not be under your control.
var in a parameter list is the Delphi syntax for call by reference. It can be typed as e.g. the AllowChange parameter in the OnChanging handler of an Listview:
procedure TSomeForm.LVOnChanging(Sender: TObject; ...; var AllowChange: Boolean);
begin
if SomeProblemOccurred then
AllowChange := False;
end;
or untyped as in your example.

Resources