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...
Oh how I wish there was something like:
ReplyDeletetype
TProp<T> = reference to property: T;
var
myProp: TProp<Boolean>;
begin
myProp := myObject.SomeBooleanPropery;
end;
Surely MyObject.Bind(Src, 'SomeBooleanProperty') CAN be resilient to property renaming, it just depends on the implementation of "Bind".
ReplyDeleteI 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. ;)
That would be a nice syntax, Stefan.
ReplyDeleteJolyon, 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.
How about this. The string is still there but at a very easy to maintain location.
ReplyDeletetype
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.
You might want to look at this:
ReplyDeletehttp://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. :)
@Andreas - Good suggestion for best practice.
ReplyDelete@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?
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.
ReplyDeleteLook at Sample9 how to use it.
You'll want to answer the new survey about the next version wanted feature....
ReplyDeleteStill to use XE2 myself but it occurs to me that even
ReplyDeleteMyObject.Bind(Src, 'SomeBooleanProperty');
makes it easier to spot necessary name changes than a TDBEdit.FieldName property inside the object inspector.
@Francois - I did :)
ReplyDelete@Lachlan - I agree. I liked Andreas' suggestion of using a const. That would really improve the odds where there are multiple references.