【问题标题】:Delphi property read/writeDelphi 属性读/写
【发布时间】:2014-10-31 18:45:48
【问题描述】:

在 delphi 类中声明属性时是否可能有不同类型的结果?

例子:

property month: string read monthGet(字符串) write monthSet(整数);

在示例中,我希望使用属性月份,当我: 阅读,我得到一个字符串; SET,我设置一个整数;

【问题讨论】:

  • 没有。它不是。这就是为什么会有像AsString 这样的转换属性和函数。如果你有更新版本的 Delphi,你可以定义一个像 TMonth 这样的类型,并为不同类型的转换编写辅助函数。你有哪个版本的 Delphi?
  • 那么,我该怎么做呢?
  • FWIW,property XYZ 的约定是将 getter 定义为 function GetXYZ: SomeType,将 setter 定义为 procedure SetXYZ(const Value: SomeType)
  • Delphi/Pascal 属性与 RTTI 密切相关,因此 getter 和 setter 必须在同一类型上工作。也许,而且我至少知道一个,其他一些语言允许这种重载,但这主要是因为“属性”属性(对于那些语言)更像是一种语法糖(例如:使用分配运算符而不是用括号调用设置器)。

标签: delphi properties integer read-write


【解决方案1】:

最接近的方法是使用Operator Overloading,但Getter/Setter 必须是相同的类型。没有办法改变它。

program so_26672343;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils;

type
  TMonth = record
  private
    FValue: Integer;
    procedure SetValue( const Value: Integer );
  public
    class operator implicit( a: TMonth ): string;
    class operator implicit( a: Integer ): TMonth;
    property Value: Integer read FValue write SetValue;
  end;

  TFoo = class
  private
    FMonth: TMonth;
  public
    property Month: TMonth read FMonth write FMonth;
  end;

  { TMonth }

class operator TMonth.implicit( a: TMonth ): string;
begin
  Result := 'Month ' + IntToStr( a.Value );
end;

class operator TMonth.implicit( a: Integer ): TMonth;
begin
  Result.FValue := a;
end;

procedure TMonth.SetValue( const Value: Integer );
begin
  FValue := Value;
end;

procedure Main;
var
  LFoo: TFoo;
  LMonthInt: Integer;
  LMonthStr: string;
begin
  LFoo := TFoo.Create;
  try
    LMonthInt := 4;
    LFoo.Month := LMonthInt;
    LMonthStr := LFoo.Month;
  finally
    LFoo.Free;
  end;
end;

begin
  try
    Main;
  except
    on E: Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;

end.

【讨论】:

  • 我同意这是一种可能,我也想过,但是相比两个属性或者直接调用setter和getter,ISTM有点过头了。
  • 如果您查看应用程序的整个概念,这并不过分。域类型(ValueObjects)只定义一次,并在整个应用程序中使用。这个小样本确实看起来像向鸟类发射火箭
  • 如果应用程序将 Month 作为中心类型之一,那么它是有意义的。否则它有点太多了,IMO。毫无疑问,将类型定义为带有隐式转换器的记录是一个优雅的概念。
  • 我在很多事情上都使用了这个解决方案,包括我自己的TDateTimeRecTDateRecTTimeRec 等。TVersionTConnectionString 也一样,所有记录都带有隐式函数。这种将一个值轻松转换为不同类型的巧妙方法 :-) 还允许我放入一些额外的函数,例如 TDateTimeRec.FriendlyDate: String;,它返回例如 Friday October 31st 2014
【解决方案2】:

这是不可能的。但是属性不一定要直接对应内部存储,所以你可以这样做:

private
  FMonth: Integer;
  function GetMonthName: string;
...
  property Month: Integer read FMonth write FMonth;
  property MonthName: string read GetMonthName;
...

procedure TMyClass.GetMonthName: string;
begin
  // code that finds name that corresponds to value of FMonth and returns it in Result.
end;

换句话说,您必须使用两个属性,一个只写(或普通),一个只读。

【讨论】:

  • 对不起,在你出现之前我已经开始写答案了。
  • 没问题。解决方案很明显,所以两个或更多得出这个结论并不意外。
【解决方案3】:

您不能在 Delphi 中直接执行此操作。


你可以做的是有一个“铸造属性”,比如:

private
  //...
  intMonth: integer
  //...
public
  //...
  property StrMonth: string read GetStrMonth write SetStrMonth;
  property IntMonth: integer read intMonth write intMonth;
  //...
end;

function YourClass.GetStrMonth: string;
begin
  case intMonth of
    1: Result := "January";
    //...
  end;
end;

procedure YourClass.SetStrMonth(Value: string);
begin
  if StrMonth = "January" then
    intMonth := 1;
    //...
  end;
end;

【讨论】:

    【解决方案4】:

    没有办法为属性做到这一点。一个属性只有一个类型。

    实现目标的明显方法是拥有可直接使用的 getter 和 setter 函数。

    function GetMonth: string;
    procedure SetMonth(Value: Integer);
    

    您可能决定将类型作为名称的一部分,以减少调用代码中的混淆。说GetMonthStrSetMonthOrd

    您可以将这些函数公开为两个单独的属性。一个只读,另一个只写。

    【讨论】:

    • “您可以将这些函数公开为两个独立的属性” 好主意 ;-)
    • @JanDoggen 正是我提出的解决方案。 :D
    猜你喜欢
    • 2014-12-07
    • 1970-01-01
    • 2010-12-02
    • 1970-01-01
    • 2012-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多