6

is it possible to have different kind of results when declaring property in delphi class?

Example:

property month: string read monthGet(string) write monthSet(integer);

In the example, I want, with the property month, that when I : READ, I get a string; SET, I set an integer;

JimmyB
  • 12,101
  • 2
  • 28
  • 44
img.simone
  • 632
  • 6
  • 10
  • 23
  • 1
    No. It is not. That's why there are casting properties and functions like `AsString`. If you have more recent version of Delphi, you can define a type like `TMonth` and write helper functions for different sorts of casting. Which version of Delphi do you have ? – TLama Oct 31 '14 at 10:26
  • So, How can I do it? – img.simone Oct 31 '14 at 10:27
  • Just look at the docs http://docwiki.embarcadero.com/RADStudio/en/Operator_Overloading_(Delphi) – Sir Rufo Oct 31 '14 at 10:34
  • FWIW, the convention for `property XYZ` is to define the getter as `function GetXYZ: SomeType` and the setter as `procedure SetXYZ(const Value: SomeType)`. – Rudy Velthuis Oct 31 '14 at 11:50
  • Delphi/Pascal properties are deeply linked to RTTI so the getter and the setter have to work on the same type. Maybe, and I know at least one, some other lang allow such overloading, but it's mostly because the "property" attribute (for those lang.) is more a syntactic sugar (e.g: using the assign operator instead of calling the setter with parenthesis). – Abstract type Oct 31 '14 at 12:09
  • You could also resort to `Variant` type. – Free Consulting Oct 31 '14 at 17:15
  • @FreeConsulting - Then (s)he would have two problems instead of one. – mg30rg Nov 04 '14 at 08:33

4 Answers4

8

The closest you can get is to use Operator Overloading but the Getter/Setter must be the same type. There is no way to change that.

program so_26672343;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  TMonth = record
  private
    FValue: Integer;
    procedure SetValue( const Value: Integer );
  public
    class operator implicit( a: TMonth ): string;
    class operator implicit( a: Integer ): TMonth;
    property Value: Integer read FValue write SetValue;
  end;

  TFoo = class
  private
    FMonth: TMonth;
  public
    property Month: TMonth read FMonth write FMonth;
  end;

  { TMonth }

class operator TMonth.implicit( a: TMonth ): string;
begin
  Result := 'Month ' + IntToStr( a.Value );
end;

class operator TMonth.implicit( a: Integer ): TMonth;
begin
  Result.FValue := a;
end;

procedure TMonth.SetValue( const Value: Integer );
begin
  FValue := Value;
end;

procedure Main;
var
  LFoo: TFoo;
  LMonthInt: Integer;
  LMonthStr: string;
begin
  LFoo := TFoo.Create;
  try
    LMonthInt := 4;
    LFoo.Month := LMonthInt;
    LMonthStr := LFoo.Month;
  finally
    LFoo.Free;
  end;
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;

end.
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • 1
    I agree that this is a possibility, and I thought of it too, but ISTM a little bit too much, compared to two properties or calling setters and getters directly. – Rudy Velthuis Oct 31 '14 at 10:59
  • If you look at the whole concept of an application it is not too much. The domain-types (ValueObjects) are defined only once and were used all over the application. This small sample indeed looks like firing rockets on birds – Sir Rufo Oct 31 '14 at 11:08
  • If the application has Month as one of the central types, then it makes sense. Otherwise it is a bit too much, IMO. It is an elegant concept to define a type as a record with implicit converters, no doubt. – Rudy Velthuis Oct 31 '14 at 11:47
  • I've used this solution for many things, including my own `TDateTimeRec`, `TDateRec`, `TTimeRec`, etc. Also the same for `TVersion` and `TConnectionString`, all records with implicit functions. Such a clever way of casting one value easily into different types :-) Also allowed me to put in some extra functions such as `TDateTimeRec.FriendlyDate: String;` which returns for example `Friday October 31st 2014` – Jerry Dodge Oct 31 '14 at 14:33
4

That is not possible. But properties do not have to correspond to internal storage directly, so you can do:

private
  FMonth: Integer;
  function GetMonthName: string;
...
  property Month: Integer read FMonth write FMonth;
  property MonthName: string read GetMonthName;
...

procedure TMyClass.GetMonthName: string;
begin
  // code that finds name that corresponds to value of FMonth and returns it in Result.
end;

In other words, you'll have to use two properties, one write-only (or normal), one read-only.

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
  • Sorry I have started to write my answer before yours appeared. – mg30rg Oct 31 '14 at 10:38
  • 2
    No problem. The solution is pretty obvious, so that two or more came to that conclusion is not unexpected. – Rudy Velthuis Oct 31 '14 at 11:00
  • If you allow both writing to the field directly and reading from it you could skip the first property and expose the field instead. I agree that yours is the book solution, but I'm reluctant to define a property for each and every field of my form. – stevenvh May 24 '22 at 08:18
1

There's no way to do that for a property. A property has a single type.

The obvious way to achieve you goal is to have getter and setter functions that you use directly.

function GetMonth: string;
procedure SetMonth(Value: Integer);

You might decide to make the type part of the name to reduce confusion in the calling code. Say GetMonthStr and SetMonthOrd.

You could expose these functions as two separate properties. One read only, the other write only.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
1

You can't directly do that in Delphi.


What you can do is having a "casting property" like:

private
  //...
  intMonth: integer
  //...
public
  //...
  property StrMonth: string read GetStrMonth write SetStrMonth;
  property IntMonth: integer read intMonth write intMonth;
  //...
end;

function YourClass.GetStrMonth: string;
begin
  case intMonth of
    1: Result := "January";
    //...
  end;
end;

procedure YourClass.SetStrMonth(Value: string);
begin
  if StrMonth = "January" then
    intMonth := 1;
    //...
  end;
end;
mg30rg
  • 1,311
  • 13
  • 24
  • You can't use the same name for a field and its property; it won't compile. Capitalizing the property won't save you either as Pascal is case-insensitive. Best practice is to prefix the field with an "F", like Rudy did. – stevenvh May 24 '22 at 08:24