开发者

Float property non-zero default, is it possible?

I would like to use a float property in my component, but set it to some non-zero default value (let's say it is 1000.0). If I try to do this in the Create, the property start to behave wildly since the default value for floats it 0 (see classes.TWriter.WriteProperty.WriteFloatProp.IsDefaultValue) so when I override some value with 0 in the form designer, delphi doesn't save this value (it's default in this case), but my Create will set it to 1000.0 when the compoent will be loaded the next time, so the actually I have the value I didn't set.

The problem is that there is no way to set d开发者_StackOverflow社区efault with 'default' directive (the compiler says 'Default values must be of ordinal, pointer or small set type') and it's also not possible to force storing with stored directive, it doesn't work (Delphi 5)

So is there a chance to find a workaround?

Thanks,

Max


Maybe you can used the stored directive:

property MyFloat: Float read GetValue write SetValue stored IsMyFloatStored;

with a Boolean function IsMyFloatStored that returns True iff MyFloat doesn't have its default value.


I fixed this by implementing the DefineProperties method in my descendant of TPersistent. I wanted to make a general solution to this problem that I could easily implement in all of my objects for all the relevant properties. Here's how it looks:

  TOverridePropFiler = class
    private
      FReadWritePropName: string;
      FObject: TObject;
      procedure ReadOverrideProp(Reader: TReader);
            procedure WriteOverrideProp(Writer: TWriter);
    public
      constructor Create(Filer:TFiler; Obj:TObject; Name:string);
    end;

  { TOverridePropFiler }

  constructor TOverridePropFiler.Create(Filer:TFiler; Obj:TObject; Name:string);
  begin
    FReadWritePropName:=Name;
    FObject:=obj;
    if (Name='') or not Assigned(GetPropInfo(Obj,Name)) then
      Raise Exception.CreateFmt('Property %s not found in object %s',[Name,Obj.ClassName]);
    Filer.DefineProperty(Name, ReadOverrideProp, WriteOverrideProp,
      GetPropValue(FObject,FReadWritePropName,False)<>uiFloat);
  end;

  procedure TOverridePropFiler.ReadOverrideProp(Reader: TReader);
  begin
    SetPropValue(FObject,FReadWritePropName,Reader.ReadFloat);
  end;

  procedure TOverridePropFiler.WriteOverrideProp(Writer: TWriter);
  begin
    Writer.WriteDouble(GetPropValue(FObject,FReadWritePropName,False));
  end;

I call it in my DefineProperties method as follows:

  procedure TMyObj.DefineProperties(Filer: TFiler);
  begin
    inherited;
    TOverridePropFiler.Create(Filer,Self,'dTOverride').Free;
  end;

To make this generalized, I had to use RTTI and save the property name locally (It's not possible to get this out of TWriter, and requires a hack for TReader). To make it thread safe (and because TFiler.DefineProperty wants "of object" functions) I encapsulated the whole thing in an object.

In my case I want the default value to be uiFloat. This constant can be set to whatever you want. If you want different defaults for different properties, you could easily add the default as a parameter to the Create function.

Note that you still have to set the property to the default in TMyObj's constructor.

This seems to me to be a fairly efficient way to work around a pretty serious limitation in Delphi.

Edit: You need to remember to add "stored false" to all of the properties that use this.


Just came across this problem and solved it by setting the value I want as default in the constructor and using the nodefault directive for that property.


As the compiler says, floating-point values cannot have a default value. The only thing I can think of is to store the floating-point value in a scaled integer. For instance, if you only need three decimals of yourFloat, you can store yourInt = 1000 * yourFloat instead. The default value would then be yourInt := 1000000, corresponding to yourFloat := 1000.000.

Of course, you can do this a bit more elegantly, like

  private
    FMyFloat: real;
    function GetValue: integer;
    procedure SetValue(Value: integer);

  published
    property MyInteger: integer read GetValue write SetValue default 1000000;

implementation

function TMyClass.GetValue: integer;
begin
  result := round(1000 * FMyFloat);
end;

procedure TMyClass.SetValue(Value: integer);
begin
  FMyFloat := Value / 1000;
end;

This way you will still only see the floating-point field FMyFloat from inside the class itself. But the property will be a scaled integer, and thus perfectly storable.


The Default value actually only tells the stream code what the default value is, and if it is that value, then it is not streamed. This saves having to stream all the default values. The component must actually set the default in the .Create anyway. The compiler does not do any magic to set the default for you.

In this case, it will not matter, as you can set the default value in your .Create constructor, and then just leave out the default in the definition. The value will always be stored, but it will work as you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜