代码之家  ›  专栏  ›  技术社区  ›  jpfollenius Rob Kennedy

德尔菲:课堂记录

  •  3
  • jpfollenius Rob Kennedy  · 技术社区  · 16 年前

    以下情况:

    type
      TRec = record
        Member : Integer;
      end; 
    
      TMyClass = class
      private
        FRec : TRec;
      public
        property Rec : TRec read FRec write FRec;
      end;
    

    以下内容不起作用(无法将左侧指定给),这是正常的,因为 TRec 是一种值类型:

    MyClass.Rec.Member := 0;
    

    但在D2007中,以下操作确实有效:

    with MyClass.Rec do
      Member := 0;
    

    不幸的是,它在D2010中不起作用(我假设它在D2009中也不起作用)。第一个问题:为什么?是否有意更改?或者这只是其他变化的副作用?D2007解决方案只是一个“bug”吗?

    第二个问题:您认为以下解决方案如何?使用安全吗?

    with PRec (@MyClass.Rec)^ do
      Member := 0;
    

    我在这里谈论的是现有的代码,因此为了使其正常工作而必须进行的更改应该是最小的。

    6 回复  |  直到 16 年前
        1
  •  6
  •   Uli Gerhardt    16 年前

    MyClass.Rec.Member := 0;
    

    不

    两种安全解决方案是:

    1. MyClass.Rec 到一个临时记录,您可以对其进行操作并重新分配 MyClass.Rec .
    2. 暴露 TMyClass.Rec.Member
        2
  •  3
  •   Brian Frost    16 年前

    在这样一些情况下,类的记录需要“直接操纵”,我经常采用以下方法:

    PMyRec = ^TMyRec;
    TMyRec = record
      MyNum : integer
    end;
    
    TMyObject = class( TObject )
    PRIVATE
      FMyRec : TMyRec;
      function GetMyRec : PMyRec;
    PUBLIC
      property MyRec : PMyRec << note the 'P'
        read GetMyRec;
    end;
    
    function TMyObject.GetMyRec : PMyRec; << note the 'P'
    begin
      Result := @FMyRec;
    end;
    

    这样做的好处是,您可以利用Delphi自动取消引用来对每个记录元素进行可读代码访问,即:

    MyObject.MyRec.MyNum:=123;

    我记不起来了,但也许用这种方法可以奏效——我尽量不使用它!

        3
  •  1
  •   Community Mohan Dere    9 年前

    无法直接分配的原因是 here .
    至于WITH,它在D2009中仍然可以工作,我希望它在D2010中也可以工作(我现在无法测试)。
    更安全的方法是直接公开记录属性,正如Allen在上述SO中所建议的那样 post :

    property RecField: Integer read FRec.A write FRec.A;
    
        4
  •  0
  •   Robert Giesecke    16 年前

    记录是 价值观

    他们甚至有复制语义赋值!这就是为什么不能就地更改属性值。因为它会违反FRec的值类型语义,并破坏依赖于它的代码是不可变的或至少是安全副本。

    这里的问题是,为什么您需要一个值(您的TRec)来表现为一个对象/实体?

    如果“TRec”正是您使用它的目的,那么它是否更适合作为一个类呢?

        5
  •  0
  •   Gerry Coll    16 年前

    更改它的原因是它是一个编译器错误。它编译的事实并不能保证它会工作。

    unit Unit2;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
    
    type
      TForm2 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        FPoint: TPoint;
        function GetPoint: TPoint;
        procedure SetPoint(const Value: TPoint);
        { Private declarations }
      public
        { Public declarations }
        property Point : TPoint read GetPoint write SetPoint;
      end;
    
    var
      Form2: TForm2;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm2.Button1Click(Sender: TObject);
    begin
      with Point do
      begin
        X := 10;
        showmessage(IntToStr(x)); // 10
      end;
    
      with Point do
        showmessage(IntToStr(x)); // 0
    
      showmessage(IntToStr(point.x)); // 0
    end;
    
    function TForm2.GetPoint: TPoint;
    begin
      Result := FPoint;
    end;
    
    procedure TForm2.SetPoint(const Value: TPoint);
    begin
      FPoint := Value;
    end;
    
    end.
    

    您的代码会突然中断,您会首先责怪Delphi/Borland允许它。

    使用Brian的建议返回一个指针,但删除带-you can eaisly do Point.X:=10;

        6
  •  -2
  •   Remko    16 年前

    另一种解决方案是使用辅助函数:

    procedure SetValue(i: Integer; const Value: Integer);
    begin
      i := Value;
    end;
    SetValue(MyClass.Rec.Member, 10);
    

    type
      TRec = record
        Member : Integer;
        Member2 : Integer;
      end;
    
      TMyClass = class
      private
        FRec : TRec;
        function GetRecByPointer(Index: Integer): Integer;
        procedure SetRecByPointer(Index: Integer; const Value: Integer);
      public
        property Rec : TRec read FRec write FRec;
        property RecByPointer[Index: Integer] : Integer read GetRecByPointer write SetRecByPointer;
      end;
    
    function TMyClass.GetRecByPointer(Index: Integer): Integer;
    begin
      Result := PInteger(Integer(@FRec) + Index * sizeof(PInteger))^;
    end;
    
    procedure TMyClass.SetRecByPointer(Index: Integer; const Value: Integer);
    begin
      PInteger(Integer(@FRec) + Index * sizeof(PInteger))^ := Value;
    end;
    

    它假设记录的每个成员都是(P)整数大小,如果不是,则AV将崩溃。

      MyClass.RecByPointer[0] := 10;  // Set Member
      MyClass.RecByPointer[1] := 11;  // Set Member2
    

    const
      Member = 0;
      Member2 = Member + sizeof(Integer);  // use type of previous member
    
      MyClass.RecByPointer[Member] := 10;
    
        function TMyClass.GetRecByPointer(Index: Integer): Integer;
        begin
          Result := PInteger(Integer(@FRec) + Index)^;
        end;
    
        procedure TMyClass.SetRecByPointer(Index: Integer; const Value: Integer);
        begin
          PInteger(Integer(@FRec) + Index)^ := Value;
        end;
    
    MyClass.RecByPointer[Member1] := 20;
    
    推荐文章