Delphi Programming

and software in general.

Tuesday, June 14, 2011

Finding yourself in a property bind

From the wishful thinking department, I would love to be able to
var  
  Src: TSomeClass;
  MyObject : TBoundObject<Boolean>;
begin
  MyObject.Bind(Src, CompilerMagic(TSomeClass.SomeBooleanProperty));
end;
instead of
var  
  Src: TSomeClass;
  MyObject : TBoundObject<TSomeClass, Boolean>;
begin
  MyObject.Bind(Src, 'SomeBooleanProperty');
end;
Why? Because the first one is resilient to property renaming.
Until I can, I am stuck with using something similar to
var
  Src: TSomeClass;
  MyObject : TBoundObject<TSomeClass, Boolean>;
begin
  MyObject.Bind(Src,
    function (const Ref:TSomeClass):Boolean // reads the bound prop
    begin
      Result = Ref.SomeBooleanProperty;
    end,
    procedure (const Ref: TSomeClass; const Value:Boolean)  // writes the bound prop
    begin
      Ref.SomeBooleanProperty := Value;
    end);
end;
which actually has some benefits - such as being able to sanitize assigned values - but is way too verbose.

I wish...

10 comments:

  1. Oh how I wish there was something like:

    type
      TProp<T> = reference to property: T;

    var
      myProp: TProp<Boolean>;
    begin
      myProp := myObject.SomeBooleanPropery;
    end;

    ReplyDelete
  2. Surely MyObject.Bind(Src, 'SomeBooleanProperty') CAN be resilient to property renaming, it just depends on the implementation of "Bind".

    I guess you mean having an earlier failure case, i.e. compilation failure, rather than a unit test failure with a "property does not exist" exception.

    But if you have that form of resilience (which is better than NO resilience), then isn't what you gain on the swings (less verbose = greater clarity in the code) worth what you trade on the roundabouts (failure during testings vs compilation).

    I also am assuming that you have unit tests in place. ;)

    ReplyDelete
  3. That would be a nice syntax, Stefan.

    Jolyon, In theory, the unit test would catch the missing string change, but I have yet to see a project of a decent size, developed under time pressure, that has 100% test coverage. Sad, but true.

    Strong typing implies spotting such errors at compile time, and that is my lament. I get by without property binding - but it would be a great asset.

    ReplyDelete
  4. How about this. The string is still there but at a very easy to maintain location.

    type
      TTestObject = class(TObject)
      private
        FSomeBoolean: Boolean;
      public
        property SomeBoolean: Boolean
          read FSomeBoolean write FSomeBoolean;
          const SomeBooleanProperty = 'SomeBoolean';
      end;

    procedure Main;
    var
      Src: TTestObject;
      MyObj: TBoundObject;
    begin
      Src := TTestObject.Create;
      MyObj := TBoundObject.Create;
      MyObj.Bind(Src, TTestObject.SomeBooleanProperty);
    end;

    Unfortunately Delphi's RTTI doesn't contain any class property information. Otherwise the string could be eliminated by using a class property and initializing them in a class constructor.

    ReplyDelete
  5. You might want to look at this:

    http://code.google.com/p/delphisorcery/source/browse/trunk/source/Library/System/System.Properties.pas

    It's far from being stable but to get an idea of what is possible. :)

    ReplyDelete
  6. @Andreas - Good suggestion for best practice.

    @Stefan - This looks really interesting. What kind of stability issues are there? Can it be made stable based on the current compiler and RTTI? Also - any usage examples?

    ReplyDelete
  7. Stability issues in the case of I only tested it with 2 objects and 2 properties of Integer and string and I have no clue how it turns out with tons of objects, other types and more complex life cycles.

    Look at Sample9 how to use it.

    ReplyDelete
  8. You'll want to answer the new survey about the next version wanted feature....

    ReplyDelete
  9. Still to use XE2 myself but it occurs to me that even

    MyObject.Bind(Src, 'SomeBooleanProperty');

    makes it easier to spot necessary name changes than a TDBEdit.FieldName property inside the object inspector.

    ReplyDelete
  10. @Francois - I did :)
    @Lachlan - I agree. I liked Andreas' suggestion of using a const. That would really improve the odds where there are multiple references.

    ReplyDelete