• 当泛型遇上结构体...


    一般使用泛型都是放类啊, 基础类型什么的, 所以使用上和非泛型基本没什么区别, 不过昨天有人在群里问的问题到是暴露了一个特例:

      TRC = record
        Str: string;
        Int: Integer;
      end;
    
    ...
    
    var
      nRC: TRC;
      nList: TList<TRC>;
      i: Integer;
    begin
      nList := TList<TRC>.Create;
      for i := 0 to 9 do
      begin
        with nRC do
        begin
          Str := IntToStr(i);
          Int := i;
        end;
        nList.Add(nRC);
      end;
    
      nList[3].Str := 'ABC'; {这里会提示左边不允许赋值}
    
      nList.Free;
    end;

    显然, 如果不是一个结构体而是一个类型就没问题了, 先不说原因, 这里我先列出思路:

    首先编译器不允许赋值, 那么如果是属性呢?

      TRC = record
      private
        FStr: string;
      public
        Int: Integer;
        property Str: string read FStr write FStr;
      end;

    改成这样...可惜仍然不行, 继续

      TRC = record
      private
        FStr: string;
        function GetStr: string;
        procedure SetStr(const Value: string);
      public
        Int: Integer;
        property Str: string read GetStr write SetStr;
      end;
    
    ...
    
    function TRC.GetStr: string;
    begin
      Result := FStr;
    end;
    
    procedure TRC.SetStr(const Value: string);
    begin
      FStr := Value;
    end;
    
    ...
    
    var
      nRC: TRC;
      nList: TList<TRC>;
      i: Integer;
    begin
      nList := TList<TRC>.Create;
      for i := 0 to 9 do
      begin
        with nRC do
        begin
          Str := IntToStr(i);
          Int := i;
        end;
        nList.Add(nRC);
      end;
    
      ShowMessage(nList[3].Str);
      nList[3].Str := 'ABC';
      ShowMessage(nList[3].Str);
      nList.Free;
    end;

    这样, 编译器终于不报错了, 可惜赋值完全无效...2次的Showmessage都是3

    OK, 现在开始分析一下具体是怎么执行的

    Unit1.pas.54: nList[3].Str := 'ABC';
    004B4C74 8D4DE8           lea ecx,[ebp-$18]
    004B4C77 BA03000000       mov edx,$00000003
    004B4C7C 8BC6             mov eax,esi
    004B4C7E E83D0F0000       call Generics + $4B5BC0
    004B4C83 8D45E8           lea eax,[ebp-$18]
    004B4C86 BA204D4B00       mov edx,$004b4d20
    004B4C8B E8B0000000       call Generics + $4B4D40

    显然, 一开始先吧list里的内容复制出来一份临时数据, 然后给这个临时数据赋值, 然后...就没有然后了

    只是改变的临时数据, list里的内容完全没变化啊

    于是, 瞬间联想到, 如果一个类实例里的某一属性是结构体的话, 那么对其赋值和取值, 也是基于创建一个临时变量,修改其内容再全部替换的过程

    这样就好解释多了, 看来是不能直接修改结构体的单独一项, 那咱们换个方法

      TRC = record
        Str: string;
        Int: Integer;
      end;
    
    ...
    
    var
      nRC: TRC;
      nList: TList<TRC>;
      i: Integer;
    begin
      nList := TList<TRC>.Create;
      for i := 0 to 9 do
      begin
        with nRC do
        begin
          Str := IntToStr(i);
          Int := i;
        end;
        nList.Add(nRC);
      end;
    
      ShowMessage(nList[3].Str);
      nRC := nList[3];
      nRC.Str := 'ABC';
      nList[3] := nRC;
      ShowMessage(nList[3].Str);
      nList.Free;
    end;

    这样果然没问题了

    至于结构体为啥不能再类实例里直接修改其内部元素的问题, 这个有待继续研究...

  • 相关阅读:
    WebView与 JS 交互方式
    ES6常用知识点
    Flash Builder 破解与开发环境配置
    如何修改默认浏览器
    基本数据类型和常用内建对象
    VS 2010 使用技巧
    javascript之事件驱动编程的几个基本概念
    javascript笔记之DHTML基础
    浏览器对象模型
    开发心得记录
  • 原文地址:https://www.cnblogs.com/lzl_17948876/p/3579698.html
Copyright © 2020-2023  润新知