Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2016-01

Property overloading vs. property array enumerators

8 messages · 4 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Ondrej Pokorny

To continue from http://bugs.freepascal.org/view.php?id=28820#bugnotes

   TTest = class
   public
     property Objects[Index: Integer]: TObject read GetObject;
   published
     property Objects: TTestObjectEnumerator read GetObjectEnumerator;
   end;

@Maciej >> But what for "write"? ;d

Your problem... I don't see the point.

Btw. if the compiler should support property overloading then the enumerator keyword doesn't make much sense because of ambiguous code.

   TTest = class
   public
     property Objects[Index: Integer]: TObject read GetObject enumerator
GetObjectEnumerator1;
     property Objects: TTestObjectEnumerator read GetObjectEnumerator2;
   end;
What should then be picked up by:
   for Object in Test.Objects do

GetObjectEnumerator1 or GetObjectEnumerator2?

Ondrej

Reply by email

Ondrej Pokorny
to Ondrej Pokorny

@Michael:

Ondrej, if you make it an interface,

   TTest = class
   public
     property Objects[Index: Integer]: TObject read GetObject;
   published
     property Objects: IEnumerator read GetObjectEnumerator;
   end;

Then the compiler will know how to free it.

Yes, true. The same is for enumerators defined as records. I forgot that it's the programmer's responsibility to use the correct scenario. So using an interface/record in case of property overload is valid. Thanks for emphasizing it. Ondrej

Reply by email

Anthony Walter
to Ondrej Pokorny

Here is a preferable solution:

    property Controls: IControlEnumerator read GetItems;

This allows for both:

  for C in Test.Controls do ShowMessage(C.Name);
  // or
  for I := 0 to Test.Controls.Count - 1 do ShowMessage(Test.Controls[I]);

That is you can access the property as either an enumerable collection, or and an indexed collection. Note it has support for a default indexer property and a count property.

This is where where:

  IControlEnumerator = IIndexedEnumerator<TControl>;

And:

type
  IIndexedEnumerator<T> = interface(IEnumerator<T>)
    function GetEnumerator: IIndexedEnumerator<T>;
    function GetCount: Integer;
    function GetItem(Index: Integer): T;
    property Count: Integer read GetCount;
    property Item[Index: Integer]: T read GetItem; default;
  end;

And finally putting together Test object as a simple example. Note that TTest has full private access to the underlying container FList:

type
  TTest = class
  private
    FList: TList<TControl>;
    function GetItems: IControlEnumerator;
  public
    constructor Create(Controls: array of TControl);
    destructor Destroy; override;
    property Controls: IControlEnumerator read GetItems;
  end;

Reply by email

Ondrej Pokorny
to Anthony Walter
Anthony Walter wrote
Here is a preferable solution: property Controls: IControlEnumerator read GetItems;

You are already the third person suggesting it :)

Yes, it is possible. You can do it with your code. I still don't know if there is a will to modify:

   TWinControl = class(TControl)
     // ...
     property Controls[Index: Integer]: TControl read GetControl;

to

   TWinControl = class(TControl)
     // ...
     property Controls: IWinControlControls read GetControls;
Something like
   TWinControl = class(TControl)
     // ...
     property Controls[Index: Integer]: TControl read GetControl
enumerator GetControlEnumerator;
or
   TWinControl = class(TControl)
     // ...
     property Controls[Index: Integer]: TControl read GetControl;
     property Controls: TControlEnumerator read GetControlEnumerator;

seems better suited from my POV.

(The same for TStrings.Objects etc.)

Ondrej

Reply by email

Maciej Izak
to Ondrej Pokorny

Another option :) from bugtracker by Kazantsev Alexey ( http://bugs.freepascal.org/view.php?id=28820#c89471 ) :

All you need is [s]love[/s] overloaded default properties. As in Delphi.

type

TObj = Class

   Type
    TValue = Type String;
    TValues = Record
     Function GetEnumerator : TEnumerator;
     Property Items[AIndex : Integer] : TValue Read GetValueByIndex Write
SetValueByIndex; Default;
     Property Items[Const AName : String] : TValue Read GetValueByName
Write SetValueByName; Default;
    End;
   Property Values : TValues Read GetValues;
 End;
var v : TObj.TValue;
    obj : TObj;
begin
  obj.Values[1] := '1';
  obj.Values['2'] := '2';
  for v in Obj.Values do;
end.
Best regards, Maciej Izak

Reply by email

Kazantsev Alexey
to Ondrej Pokorny
Ondrej Pokorny wrote
why the double-posting?

Because my example answer to your querys:

First, I don't want to publish the TEnumeratedObjects object to the outside of TTest (e.g. you could call Free on it and do other stuff I don't want to be allowed).

You can return record, and what is more, record of nested private type (i.e. user can use your type, but can't declare variable of your type).

Second, TEnumeratedObjects doesn't even need to exist! What about "virtual" array properties that are not mapped to existing objects?

It because your example very simple. In more complex cases we can have other methods/overloaded operators in returned record. And it more flexible solution.

TValueArray = Array Of TObj.TValue;
var va : TValueArray;
Obj.Values.Sort;
va := Obj.Values;
Kazantsev Alexey

Reply by email

Ondrej Pokorny
to Kazantsev Alexey
Kazantsev Alexey wrote 28 lines
On Thu, 28 Jan 2016 22:33:39 +0400, Ondrej Pokorny wrote: > why the double-posting? Because my example answer to your querys: > First, I don't want to publish the TEnumeratedObjects object to the > outside of TTest (e.g. you could call Free on it and do other stuff I > don't want to be allowed). You can return record, and what is more, record of nested private type (i.e. user can use your type, but can't declare variable of your type). > Second, TEnumeratedObjects doesn't even need to exist! What about > "virtual" array properties that are not mapped to existing objects? It because your example very simple. In more complex cases we can have other methods/overloaded operators in returned record. And it more flexible solution. TValueArray = Array Of TObj.TValue; var va : TValueArray; Obj.Values.Sort; va := Obj.Values;

Yes. I see. But I want to add an enumerator to an array property. TValues is not an array property. It is a record... As I said before there are some cases I don't want any object/interface/record/banana in between. I know I can do that the other way. It doesn't matter if you do it with private record or generic interface or a banana.

I want A and people suggest me B and try to convince me that B is better and more flexible. But I don't want B, I want A :)

Ondrej

Reply by email