Generics object and assignment overloading

Generics object and assignment overloading
type
  TMyObject = class
    property Value: T;
  end;
  TIntObject = TMyObject;
var
  obj: TIntObject;
  int: integer;
begin
  obj := TIntObject.Create;
  obj := 4;  // assigns 4 to obj.value
  int := obj; // resulting in Int being 4
end;
Is it possible to do operator overloads which allows the assignments as outlined above? I know this is not housebroken code - but it would help immensely for code simplicity in the specific context that I want to use it in. If possible, what are the pitfalls? (instances will not be persistent or streamed).

Comments

  1. You cannot overload assignment.  The closest you can get is to overload the implicit cast operator with operator overloading. Which leads you to this:

    {$APPTYPE CONSOLE}

    type
      TMyRec = record
        FValue: T;
        class operator Implicit(const Value: T): TMyRec;
        class operator Implicit(const Value: TMyRec): T;
      end;

    class operator TMyRec.Implicit(const Value: T): TMyRec;
    begin
      Result.FValue := Value;
    end;

    class operator TMyRec.Implicit(const Value: TMyRec): T;
    begin
      Result := Value.FValue;
    end;

    var
      value: TMyRec;
      int: integer;

    begin
      value := 666;
      int := value;
      Writeln(int);
    end.

    If only you'd asked this on Stack Overflow, others could have benefited in the future, rather than this being lost.

    ReplyDelete
  2. As David Heffernan said, not with objects, only records. If you can use interfaces, you can have your cake and eat it too.

    ReplyDelete
  3. class, not record.
    In other words - not possible?

    ReplyDelete
  4. Good point on the SO thing. I'll do that for the future.

    ReplyDelete
  5. You can use class if you have ARC. Anyway, it's really not going to hurt you to expose  the value as a property, is it!

    ReplyDelete
  6. Asbjørn Heid I require inheritance from the TMyObject class - is that doable with interfaces?

    ReplyDelete
  7. David Heffernan It would be a nicety, not a necessicty. It wouldn't hurt to get rid of the numerous .Value assignment references.

    MyClass.ThisField.Value := ref.ThisField; //int
    MyClass.ThatField.Value := ref.ThatField; //string
    contra
    MyClass.ThisField := ref.ThisField; //int
    MyClass.ThatField := ref.ThatField; // string

    There can be a lot of them.

    ReplyDelete
  8. Javier Hernández I did, I think...

    ReplyDelete
  9. Lars Fosdal Ah then it gets tricky quickly, since you don't know which one to instantiate on the implicit conversion operator from T to TRec.

    ReplyDelete
  10. Primož Gabrijelčič That doesn't look pertinent to the issue here

    ReplyDelete
  11. Lars Fosdal Wait, if you want this to be able to copy field values like that, why not just descend from TPersistent and use Assign?

    edit: sorry, silly friday suggestion. Brain is totally in weekend mode.

    ReplyDelete
  12. These are editor wrappers that I am tinkering with - i.e. lots of once-off declarations.  No point in creating assigns for the umpteen variations of classes and editor combos. All I want to do is to minimize the code - and implicit would have done the job - had it been allowed for classes.

    Code extract
    TSupplierEdit = class(TEditorGrid)
      type
        TRepStatusEdit = TEditorGrid.TEnumComboListEditor;
        TBool = TEditorGrid.TEnumComboListEditor;
      private
    procedure SetSupplier(const Value: TPSDSupplier);
      protected
        SupplierId: TIntegerEdit;
        SupplierNo: TStringEdit;
        SupplierName: TStringEdit;
        SupplierGS1No: TStringEdit;
        SupplierGLN: TStringEdit;
        OtherList: TRepStatusEdit;
        Bool: TBool;
      public
        constructor Create(const aGrid: TAdvStringGrid); override;
        property Supplier: TPSDSupplier;
      end;

    constructor TSupplierEdit.Create;
    begin
      inherited;
      SupplierId := Add('Id');
      SupplierId.ReadOnly := True;
      SupplierNo := Add('SupplierNo');
      SupplierName := Add('Name');
      SupplierGS1No := Add('GS1 No');
      SupplierGLN := Add('GLN');
      Status := AddComboList('Status');
      Status.Value := ersNew; 
      Bool := AddComboList('Can do?');
      Bool.OnElementToString :=
        function(value:Boolean):String
        begin
          case Value of
          False: Result := 'Nope';
          else Result := 'Yup';
          end;
        end;
    end;

    ...
    This is where I'd love to get rid of the .value ref.

    procedure TSupplierEdit.SetSupplier(const Value: TPSDSupplier);
    begin
      FSupplier := Value;
      SupplierEdit.SupplierId.Value := Supplier.Id;
      SupplierEdit.SupplierNo.Value := Supplier.SupplierNo;
      SupplierEdit.SupplierName.Value := Supplier.SupplierName;
      SupplierEdit.SupplierGS1No.Value := Supplier.SuppliersGS1No;
      SupplierEdit.SupplierGLN.Value := Supplier.SuppliersGLN;
      SupplierEdit.OtherList.Value := ersRetry;
      SupplierEdit.Bool.Value := True;
      SupplierEdit.PopulateGrid;
    end;

    No, I don't want to use RTTI - as that only would work it for some of the use cases - unless I could somehow pass a class.property by reference and extract the property type from it as well as capturing getter and setter.
    I am full of wishful thinking, it seems.

    ReplyDelete
  13. Any approach using a record will not work (or requires some nasty workarounds) or is just not worth it so save 6 characters of optimal code.

    What you are trying here is some kind of operator lifting for the assign operator depending on the right side of the assignment. Delphi does not support that - the closest approach I can think of is really what I did with the observable in knockoff (I also blogged about it) - but it will not be suitable for you because due to the nature of anonymous method types you cannot access any other members on such a type. But that type is not to save 6 characters anyway.

    ReplyDelete
  14. it'd possible if property could be marked as "default" like we do with indexed [] properties, but then, how do you set the obj itself?
    Looking your code, I think Supplier needs some assign method to do SupplierEdit.Assign(Supplier). At least, all .Value:= get isolated, hidden out of view.

    ReplyDelete

Post a Comment