Delphi类:Property vs Get / Set方法

时间:2015-03-05 17:59:10

标签: class delphi properties setter getter

所以我对OO编程有点新意。德尔福具有属性,旨在成为更优雅的"获取课程数据的方式比 getters / setters (在此处阅读usage of property vs getters/setters in business classes)。

我应该何时直接使用这些字段,何时应该在属性中使用 getters / setters ?我只是在需要操纵数据时才会说,但我不确定。

编辑:

省略一个除了返回字段本身值之外什么都不做的setter是错误的吗?

  property Field :integer read FField write FField;

3 个答案:

答案 0 :(得分:11)

为什么属性?

首先,来自documentation on properties的快速摘要:

  

属性(如字段)定义对象的属性。但是,虽然字段仅是可以检查和更改其内容的存储位置,但属性将特定操作与读取或修改其数据相关联。属性提供对对象属性的访问的控制,并且它们允许计算属性。

为什么不只是setter和getter?

存储和访问的分离确实可以通过使用getter和setter来实现,并且保留属性。这是事实,但你链接的问题源于语言差异:Delphi确实有属性,那里的答案已经解释了为什么要使用它们。两个最明显的原因是(1)更清晰的代码和(2)分配能力。我认为this answer已经相当广泛地处理了它。

此外,在不使用属性的情况下,总是需要getter和setter,而不是使用属性。假设一个setter实现,但没有getter:一个属性可以直接读取该字段。

课程完成

当您只声明属性的名称及其类型时,Delphi的类完成默认为读取私有字段和设置私有字段的私有setter。请注意,这只是默认配置,您可以再次根据需要进行修改。当您完全指定属性声明时,类完成将符合并添加声明所需的私有字段,getter和/或setter。

没有getter和setter的属性

  

省略一个除了返回字段本身值之外什么都不做的setter是错误的吗?

当一个属性没有getter或setter并且它只是读取和写入字段时,你可以得出结论,除了一致之外没有区别。但事实并非如此。该字段和属性具有不同的名称,因此可能具有不同的含义。意思是你可以给。请参阅Using properties instead of fields in class methods of the same unit is a bad practice?

何时使用getter或setter?

  

......我只是在需要操纵数据时才会发现......

嗯,这是部分正确的。操纵是众多原因之一。考虑Price类型的String属性及其私有字段FPrice

  • 限制:当价格需要等于或高于零时,
  • 委托:当FPrice属于另一个字段时,或者当其他字段超出此类的责任时,
  • 验证:当价格可能只有逗号后面两位小数时,
  • 解读:当价格输入数千时,但应以美分存储,
  • 效果:当价格对其他字段产生影响时,例如费率或保证金,
  • 激活:当价格编辑需要立即采取行动时,例如更新GUI,
  • 转化:当以美元输入价格但应以日元存储时,
  • 取消:当价格没有意义时,例如以科学记数法输入时。

请注意,Price属性非常简陋。离开它的二传手或吸气剂以便将来实施是非常有可能的。但想象一下更高级的属性,这些属性离不开setter或getter:

  • 在咨询之前需要创建的字段:

    function TMyObject.GetBarelyUsed: TRare;
    begin
      if FBarelyUsed = nil then
        FBarelyUsed := TRare.Create(Self);
      Result := FBarelyUsed;
    end;
    
  • 可以选择项目,但项目本身不知道该怎么做。而是所有者。注意在这种情况下完全没有私有字段:

    procedure TItem.SetSelected(Value: Boolean);
    begin
      if Value <> Selected then
      begin
        if Value then
          Owner.Selection.Add(Self)
        else
          Owner.Selection.Remove(Self);
      end;
    end;
    
  • 图像控件,专门用于查看您自己的图像格式。 FileName属性的分配包括:检查正确的文件扩展名,检查文件存在,将文件名存储在私有字段中,加载文件,调整图片的维度,或者撤消先前的分配:

    procedure TAwDxfImage.SetFileName(const Value: TFileName);
    begin
      if FFileName <> Value then
        if SameText(ExtractFileExt(Value), '.' + SDxf) and
          FileExists(Value) then
        begin
          FFileName := Value;
          FGraphic.LoadFromFile(FFileName);
          FGraphic.SetBounds(Width, Height);
        end
        else
        begin
          FFileName := '';
          FGraphic.Clear;
        end;
    end;
    

<子> Source: NLDelphi

答案 1 :(得分:3)

除了@NGLN回答之外,还有另一个属性getter / setter的用例。

只能通过实例方法访问通过接口访问类实例。如果你必须在这种情况下访问属性,你必须实现getter / setter方法。

type
  IField = interface
    function GetField: integer;
    function SetField(value: integer);
    property Field: integer read GetField write SetField;
  end;

  TField = class(TInterfacedObject, IField)
  protected
    FField: integer;
    function GetField: integer;
    function SetField(value: integer);
  public
    property Field: integer read GetField write SetField;
  end;

var
  f: IField;
  x, n: integer;
...
  f := TField.Create;
  f.Field := 5;
  f.SetField(6);
  n := f.Field;
  x := f.GetField;

当然,根据您是否只需要对该属性的读取或写入权限,您可以在接口声明中省略setter或getter。

请记住,通过接口访问实例会为所有接口实现的方法提供公共可见性。这就是为什么在上面的例子中,你可以调用f.GetField,尽管它被声明为受保护(甚至是私有)。

答案 2 :(得分:1)

不可接受的是这样做

TMyClass = class
private
public
  Fubar :integer;
end;

你的例子的其余部分都没问题。我很乐意收到你这样的代码

TMyClass = class
private 
  FFu : integer;
public
  property Fu :integer read FFu write FFu;
end;

因为我可以自信地将其改为

TMyClass = class
private 
  FFu : integer;
  procedure SetFu(Value : Integer);
  function GetBar() : string;
public
  property Fu :integer read FFu write SetFu;
  property Bar : String read GetBar;
end;

不破坏现有代码。

我个人不喜欢什么都不做的制定者

procedure TMyClass.SetFu(Value : Integer);
begin
  FFu := Value;
end;

但事实上它是无害的。

这有帮助吗?

这是使用setter

的正当理由或动机
procedure TMyClass.SetFu(Value : Integer);
begin
  if FFu <> Value then begin  
    FFu := Value;
    if Assigned(FAfterFooChanged) then
      FAfterFooChanged(FFu);
  end;
end;

不是&#39;操纵&#39;就这样......