我正在尝试构建一个自定义比较器,它允许将比较功能分配给内部字段.为了简化比较器的创建,我尝试添加一个类构造函数类函数Construct来初始化比较器.

现在,如果我尝试编译以下示例,编译器将显示

[dcc32 Fehler] ConsoleDemo1.dpr(37): E2555 Symbol ‘Result’ cannot be tracked

我有以下示例代码:

program ConsoleDemo1;

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

uses
  Generics.Collections,Generics.Defaults,System.SysUtils;

type

  TConstFunc<T1,T2,TResult> = reference to function(const Arg1: T1; const Arg2: T2): TResult;

  TDemo = class(TComparer<string>)
  private
    FVar: TConstFunc<string,string,Integer>;
    function CompareInternal(const L,R: string): Integer;
  public
    class function Construct(): TDemo;
    function Compare(const L,R: string): Integer; override;
  end;

function TDemo.Compare(const L,R: string): Integer;
begin
  Result := FVar(L,R);
end;

function TDemo.CompareInternal(const L,R: string): Integer;
begin
  Result := AnsiCompareStr(L,R);
end;

class function TDemo.Construct: TDemo;
begin
  Result := TDemo.Create();
  Result.FVar := Result.CompareInternal;
end;

end.

解决方法

我不认为这是一个bug.最重要的是,您将TConstFunc定义为匿名方法类型.这些被管理,引用计数,非常特殊的类型与常规对象方法截然不同.通过编译器魔术,它们通常是分配兼容的,但有几个重要的注意事项.考虑更简洁:
program Project1;

{$APPTYPE CONSOLE}

type
  TFoo = reference to procedure;

  TDemo = class
  private
    FFoo : TFoo;
    procedure Foo;
  public
    class function Construct(): TDemo;
  end;

procedure TDemo.Foo;
begin
  WriteLn('foo');
end;

class function TDemo.Construct: TDemo;
begin
  result := TDemo.Create();
  result.FFoo := result.foo;
end;

end.

这也产生相同的编译器错误(E2555).因为成员方法是一个object(object method)类型的过程,而是将它分配给一个对过程(匿名方法)类型的引用,这相当于(我怀疑编译器正在将其扩展为):

class function TDemo.Construct: TDemo;
begin
  result := TDemo.Create();
  result.FFoo := procedure
                 begin
                   result.foo;
                 end;
end;

编译器不能直接分配方法引用(因为它们是不同类型的),因此(我猜想)必须将它包装成一个隐式需要捕获结果变量的匿名方法.函数返回值不能被匿名方法捕获,但只有局部变量可以.

在你的情况下(或者,实际上,对于任何函数类型),由于匿名包装器隐藏了结果变量,所以不能表达等价物,但我们可以想象在理论上是相同的:

class function TDemo.Construct: TDemo;
begin
  Result := TDemo.Create();
  Result.FVar := function(const L,R : string) : integer
                 begin
                   result := result.CompareInternal(L,R);  // ** can't do this
                 end;
end;

正如David所说,引入一个局部变量(可以被捕获)是一个正确的解决方案.或者,如果您不需要TConstFunc类型为匿名,则可以简单地将其声明为常规对象方法:

TConstFunc<T1,TResult> = function(const Arg1: T1; const Arg2: T2): TResult of object;

尝试捕获结果的另一个示例将失败:

program Project1;

{$APPTYPE CONSOLE}

type
  TBar = reference to procedure;
  TDemo = class
  private
    FFoo : Integer;
    FBar : TBar;
  public
    class function Construct(): TDemo;
  end;

class function TDemo.Construct: TDemo;
begin
  result := TDemo.Create();
  result.FFoo := 1;
  result.FBar := procedure
                 begin
                   WriteLn(result.FFoo);
                 end;
end;

end.

这个不起作用的根本原因是因为一个方法的返回值实际上是一个var参数,匿名闭包捕获变量而不是值.这是一个关键点.同样,这也是不允许的:

program Project1;

{$APPTYPE CONSOLE}

type
  TFoo = reference to procedure;

  TDemo = class
  private
    FFoo : TFoo;
    procedure Bar(var x : integer);
  end;

procedure TDemo.Bar(var x: Integer);
begin
  FFoo := procedure
          begin
            WriteLn(x);
          end;
end;

begin
end.

[dcc32 Error] Project1.dpr(18): E2555 Cannot capture symbol ‘x’

在引用类型的情况下,如在原始示例中,您真的只对捕获引用的值而不是包含该引用的变量感兴趣.这不会使其在语法上相同,编译器为此而为您创建一个新变量是不合适的.

我们可以重写上面的内容,引入一个变量:

procedure TDemo.Bar(var x: Integer);
var
  y : integer;
begin
  y := x;
  FFoo := procedure
          begin
            WriteLn(y);
          end;
end;

这是允许的,但预期的行为将是非常不同的.在捕获x(不允许)的情况下,我们期望FFoo总是将任何变量作为参数x传递的当前值写入到Bar,而不管在此过程中哪里或何时被更改.我们也预期,即使在创建它的任何范围之后,关闭也会保持变量存活.

然而,在后一种情况下,我们预期FFoo输出y的值,它是变量x的值,就像最后一次调用Bar一样.

回到第一个例子,考虑一下:

program Project1;    
{$APPTYPE CONSOLE}    
type
  TFoo = reference to procedure;    
  TDemo = class
  private
    FFoo : TFoo;
    FBar : string;
    procedure Foo;
  public
    class function Construct(): TDemo;
  end;

procedure TDemo.Foo;
begin
  WriteLn('foo' + FBar);
end;

class function TDemo.Construct: TDemo;
var
  LDemo : TDemo;
begin
  result := TDemo.Create();
  LDemo := result;
  LDemo.FBar := 'bar';
  result.FFoo := LDemo.foo;
  LDemo := nil;
  result.FFoo();  // **access violation
end;

var
 LDemo:TDemo;
begin
  LDemo := TDemo.Construct;
end.

这里很清楚:

result.FFoo := LDemo.foo;

我们还没有为存储在LDemo中的TDemo实例分配方法foo的正常引用,但实际上已经捕获了变量LDemo本身,而不是当时包含的值.将LDemo设置为零后,自然会产生访问冲突,甚至认为在分配作业时引用的对象实例仍然存在.

这与我们简单地将TFoo定义为对象的过程而不是引用过程的行为截然不同.如果我们这样做,那么上面的代码就像一个可能天真地期待的(输出foobar到控制台).

delphi – 为什么我不能将我的函数引用分配给一个匹配的变量? E2555被提出的更多相关文章

  1. ios – 如何从变量访问属性或方法?

    是否可以使用变量作为Swift中方法或属性的名称来访问方法或属性?在PHP中,您可以使用$object->{$variable}.例如编辑:这是我正在使用的实际代码:解决方法你可以做到,但不能使用“纯粹的”Swift.Swift的重点是防止这种危险的动态属性访问.你必须使用Cocoa的Key-ValueCoding功能:非常方便,它完全穿过你要穿过的字符串到属性名称的桥,但要注意:这里是龙.

  2. iOS &gt;&gt;块&gt;&gt;更改块外部的变量值

    我不是在处理一个Object并改变它,就像我的mString一样.我希望’center’属性的行为类似于myInt,因为它是直接访问的C结构,而不是指向对象的指针.我希望’backgroundColor’的行为类似于我的imstring,因为它是一个指向一个新对象的对象的指针,不是吗?

  3. ios – Xcode Bot:如何在post触发器脚本上获得.ipa路径?

    我正在使用机器人来存档iOS应用程序,我需要获取.ipa产品路径才能将其发布到我们的分发系统中.机器人设置:并使用脚本打印所有env变量,其中不包含ipa文件的路径.此外,一些变量指向不存在的目录,即:XCS_OUTPUT_DIR这里的env变量输出:除此之外,我还能够确认.ipa文件是在另一个文件夹中创建的(/IntegrationAssets//

  4. ios – 使用附加字符串本地化Info.plist变量

    我正在尝试本地化应用程序的名称,同时仍然能够根据构建配置追加字符串.所以目前它被设置为:该设置定义为:通过这种方式,我们可以为应用程序添加后缀以用于不同的beta版本.问题是,当我们尝试本地化本地化的InfoPlist.strings中的应用程序显示名称时,就像这样我们覆盖存储在Info.plist中的值,并丢失后缀字符.这有什么好办法吗?

  5. iOS – 开始iOS教程 – 变量之前的下划线?

    这是正确的还是我做错了什么?

  6. ios – 静态计算变量被多次实例化

    我有一个日期格式化程序,我试图在UITableViewCell子类中创建一个单例,所以我创建了一个这样的计算属性:问题是我不止一次看到print语句,这意味着它不止一次被创建.我已经找到了其他方法,但我很想知道这里发生了什么.有任何想法吗?解决方法您的代码段相当于只获取属性,基本上它与以下内容相同:如果你只想运行一次,你应该像定义一个惰性属性一样定义它:

  7. ios – UIApplication.delegate必须仅在主线程中使用[复制]

    我应该在主调度中的viewControllers中声明这些)变量位置声明定义了它的范围.您需要确定这些变量的范围.您可以将它们声明为项目或应用程序级别(全局),类级别或特定此功能级别.如果要在其他ViewControllers中使用这些变量,则使用公共/开放/内部访问控制将其声明为全局或类级别.

  8. ios – 无法理解Objective-C块文档

    为什么localVariable“按价值使用?”>如果我在第二个例子中将__block存储类型添加到localVariable,我错误地假设该块关闭了变量,所以它将它保留在堆中直到块被释放?解决方法Howexactlyisoneexample“accessedbyreference”whiletheotheroneisaccessedbyvariable?self是当前正在执行找到块的方法的对象.强引用只是意味着对象的保留计数增加.IfIaddthe__blockstoragetypetolocalVar

  9. ios – 为BOOL变量编写getter和setter

    显然,使用obj-c,通常没有理由编写getter和setter(感谢有用的mr@synthesize).所以现在,需要做到这一点,我遇到了一个我不知道如何编写它们的问题.:p我敢肯定我可能不会以正确的方式解决我的问题–只是将我的对象子类化得更容易–但我正在尝试编写类别代码以添加属性,因为(在开头)它更快,因为我想学习如何在我的应用程序中使用类别代码.我有这个:我在setter中没有if查询就试过

  10. ios – 为什么不保留__block变量(在非ARC环境中)?

    我正在阅读__blockvariables上的文档,并考虑我使用__block的情况.对我来说,似乎我需要两种情况:>在块中使用时将变量标记为读写>在块内引用self时避免保留周期从表面上看,这两件事似乎并不相关.我认为__block变量没有被保留为更多的技巧我需要记住避免保留周期的特定用例.我想知道,为什么不能保留它们是否有更重要的建筑理由?

随机推荐

  1. delphi – 主窗口按进程名称处理

    DelphiXe,Win7x64如何从进程名称(exe文件的完整路径)获取主窗口句柄,或至少一个类或窗口名称(如果该进程只有一个窗口).例:解决方法我同意Petesh的说法,你需要枚举顶级窗口并检查创建它的进程的模块文件名.为了帮助您开始枚举顶级窗口,这是一个delphi实现.首先,当你回调给你时,你需要一些与EnumWindows方法通信的方式.为此声明一条记录,该记录将保存您要查找的模块的文件

  2. 如何在Delphi中纯粹通过RTTI信息(即不使用任何实际对象实例)获取TObjectList的子项类型?

    我正在使用RTTI实现用于流式传输任意Delphi对象的通用代码,并且为了使其工作(更具体地说,为了使加载部分工作),我需要以某种方式获得TObjectList的子项类型<T>不使用任何实际对象实例的字段.要求不使用任何实际对象实例的明显原因是,在从流加载对象的情况下(仅基于要加载的对象的类类型的知识),我将不会有任何实例在加载完成之前完全可用–我宁愿只能访问相关类的纯RTTI数据.我希望能

  3. inno-setup – Inno Setup – 安装程序背景图片

    图像作为安装程序背景如何用inno5.5.9做到这一点?

  4. inno-setup – Inno Setup – 如何添加多个arc文件进行解压缩?

    使用InnoSetup解压缩弧文件.我希望有可能解压缩多个arc文件以从组件选择中安装文件(例如).但仍然显示所有提取的整体进度条.这可能吗?的回答的修改预备是相同的,参考其他答案.在ExtractArc中,为要提取的每个存档调用AddArchive.

  5. delphi – 如何在DataSet的帮助下在TAdvStringGrid中显示数据库中的BLOB图像

    解决方法CreateBlobStream正在创建一个TStream对象,而不是TMemoryStream.由于您不想将JPG写入数据库,因此应使用bmRead而不是bmReadWrite.我不习惯sqlite,但你必须确保使用合适的二进制日期类型.为了确保存储的图像真的是JPG,您应该编写JPG以进行测试,例如:

  6. inno-setup – 在Inno Setup的Code部分下载程序后运行程序

    如何运行我通过Internet下载的应用程序,在代码部分中使用,并等待该应用程序完成运行.我有,使用InnoTools下载程序,下载这两个文件,我想,在第二个完成下载后运行该下载,或jdk-8u111-windows-x64.exe,然后继续安装.解决方法使用其他下载插件,而不是ITD(请参阅下面的原因).例如,InnoDownloadPlugin.当您包含idp.iss时,它定义了一个全局IDP

  7. progress-bar – Inno Setup Run部分的简单进度页面

    我的安装程序非常简单,它基本上是:>欢迎页面>进展页面>最终页面欢迎页面和最终页面是标准页面.在Progress页面,我正在静默安装一堆其他程序.实际的脚本是在[Run]部分中安装每个程序.问题是酒吧达到100%然后停留在那里.我只能更改消息文本.我想要实现的是使用Pascal脚本显示进度,例如:这样我就可以显示更准确的进度条.这就是我所拥有的:问题是,当我构建安装程序时,它不显示欢迎页面.我做错了什么?

  8. delphi – 如何使“显示/隐藏桌面图标”设置生效?

    下面的代码调用SHGetSetSettings函数来隐藏桌面图标但它只是从视图菜单中取消选中“显示桌面图标”.我打电话给SHChangeNotify;更新桌面,但这不起作用?解决方法isa,要刷新桌面,您可以将F5键发送到progman窗口隐藏桌面图标的另一种方法是再次显示

  9. inno-setup – Inno Setup – 避免显示子安装程序的文件名

    我试图使用InnoSetup–Howtohidecertainfilenameswhileinstalling?(FilenameLabel)的想法Theonlysuresolutionistoavoidinstallingthefiles,youdonotwanttoshow,usingthe[Files]section.Installthemusingacodeinstead.UsetheEx

  10. inno-setup – Inno Setup磁力链接下载实施

    我目前正在使用InnoDownloadPlugin为我的安装程序下载文件,这个问题最大的问题是faila正确下载文件.因为连接不良等诸多原因.我想添加一种替代方法来下载文件,因此用户可以选择是否需要常规方式或torrent方式.我知道我可以使用aria2c.exe应用程序(https://aria2.github.io/),有人可以帮我实现它的inno设置代码吗?我需要的是使用torrent(ar

返回
顶部