方法指针和常规过程不兼容 [英] Method pointer and regular procedure incompatible

查看:15
本文介绍了方法指针和常规过程不兼容的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序,它有多种形式.所有这些表单都有一个 PopupMenu.我以编程方式构建菜单项,所有这些都在一个公共根菜单项下.我希望所有菜单项都调用相同的过程,而菜单项本身基本上是作为一个参数....

I have an app, which has multiple forms. All these forms have a PopupMenu. I build the menu items programatically, all under a common root menu item. I want ALL the menu items to call the same procedure, and the menu item itself is basically acting as an argument....

当我只有一个表单执行此功能时,我就可以使用此功能.我现在有多种表格需要这样做.我正在将所有代码移动到一个公共单元.

I had this working when I just had one form doing this functionality. I now have multiple forms needing to do this. I am moving all my code to a common unit.

Example.
Form A has PopupMenu 1.  When clicked, call code in Unit CommonUnit.
Form B has PopupMenu 2.  When clicked, call code in unit CommonUnit.

当我需要从每个表单调用我的弹出窗口时,我会调用我的顶级过程(位于 CommonUnit 单元中),将每个表单的顶级菜单项的名称传递给公共单元中的顶级过程.

When I need to call my popup from each form, I call my top level procedure (which is in unit CommonUnit), passing the name of the top menu item from each form to the top level procedure in the common unit.

我正在使用代码向 PopupMenu 添加项目.

I am adding items to my PopupMenu with with code.

M1 := TMenuItem.Create(TopMenuItem);
M1.Caption := FieldByName('NAME').AsString;
M1.Tag := FieldByName('ID').AsInteger;
M1.OnClick := BrowseCategories1Click;
TopMenuItem.Add(M1);

我在编译时收到错误消息.具体来说,OnClick 行正在抱怨

I am getting an error message when I compile. Specifically, the OnClick line is complaining about

不兼容的类型:'方法指针和常规过程'.

Incompatible types: 'method pointer and regular procedure'.

我已经定义了 BrowseCategories1Click,就像我之前在单个表单上执行此操作时一样.唯一的区别是它现在是在一个公共单元中定义的,而不是作为表单的一部分.

I have defined BrowseCategories1Click exactly like it was before when I was doing this on a single form. The only difference is that it is now defined in a common unit, rather than as part of a form.

定义为

procedure BrowseCategories1Click(Sender: TObject);
begin
//
end;

解决这个问题最简单的方法是什么?

What is the easiest way to get around this?

谢谢GS

推荐答案

一点背景...

Delphi 有 3 种程序类型:

Delphi has 3 procedural types:

  • 独立或单元范围的函数/过程指针声明如下:

  • Standalone or unit-scoped function/procedure pointers declared like so:

var Func: function(arg1:string):string;
var Proc: procedure(arg1:string);

Method 指针声明如下:

var Func: function(arg1:string):string of object;
var Proc: procedure(arg1:string) of object;

而且,从 Delphi 2009 开始,匿名(见下文)函数/方法指针声明如下:

And, since Delphi 2009, anonymous(see below) function/method pointers declared like so:

var Func: 引用函数(arg1:string):string;
var Proc: 对过程的引用(arg1:string);

独立指针和方法指针不可互换.其原因是在方法中可访问的隐式 Self 参数.Delphi 的事件模型依赖于 method 指针,这就是为什么不能将 standalone 函数分配给对象的事件属性.

Standalone pointers and method pointers are not interchangeable. The reason for this is the implicit Self parameter that is accessible in methods. Delphi's event model relies on method pointers, which is why you can't assign a standalone function to an object's event property.

所以你的事件处理程序必须被定义为some类定义的一部分,any类定义以安抚编译器.

So your event handlers will have to be defined as part of some class definition, any class definition to appease the compiler.

正如 TOndrej 建议的那样,您可以绕过编译器,但如果这些事件处理程序在同一个单元中,那么无论如何它们应该已经相关,因此您最好继续将它们包装到一个类中.

As TOndrej suggested you can hack around the compiler but if these event handlers are in the same unit then they should already be related anyway so you may as well go ahead and wrap them into a class.

我还没有看到的另一个建议是稍微回溯一下.让每个表单实现自己的事件处理程序,但让该处理程序将责任委托给在您的新单元中声明的函数.

One additional suggestion I have not seen yet is to backtrack a little. Let each form implement its own event handler but have that handler delegate responsibility to a function declared in your new unit.

TForm1.BrowseCategoriesClick(Sender:TObject)
begin
  BrowseCategories;
end;

TForm2.BrowseCategoriesClick(Sender:TObject)
begin
  BrowseCategories;
end;

<小时>

unit CommonUnit

interface
procedure BrowseCategories;
begin
//
end;

这有一个额外的好处,就是将用户操作的响应与触发操作的控件分开.您可以轻松地将工具栏按钮和弹出菜单项的事件处理程序委托给相同的函数.

This has the added benefit of separating the response to the user's action from the control that triggered the action. You could easily have the event handlers for a toolbar button and a popup menu item delegate to the same function.

您选择哪个方向最终取决于您,但我提醒您关注哪个选项可以使将来的可维护性更容易,而不是目前最方便的选项.

Which direction you choose is ultimately up to you but I'd caution you to focus on which option will make maintainability easier in the future rather than which is the most expedient in the present.


匿名方法

匿名方法是一个完全不同的野兽.匿名方法指针可以指向独立函数、方法或内联声明的未命名函数.最后一个函数类型是他们从 anonymous 获得名称的地方.匿名函数/方法具有捕获在其作用域外声明的变量的独特能力

Anonymous methods are a different beast all together. An anonymous method pointer can point to a standalone function, a method or a unnamed function declared inline. This last function type is where they get the name anonymous from. Anonymous functions/methods have the unique ability to capture variables declared outside of their scope

function DoFunc(Func:TFunc<string>):string
begin
  Result := Func('Foo');
end;

// elsewhere
procedure CallDoFunc;
var
  MyString: string;
begin
  MyString := 'Bar';
  DoFunc(function(Arg1:string):string
         begin
           Result := Arg1 + MyString;
         end);
end;

这使它们成为最灵活的过程指针类型,但它们也可能有更多的开销.变量捕获和内联声明一样消耗额外的资源.编译器对内联声明使用隐藏的引用计数接口,这会增加一些小开销.

This makes them the most flexible of the procedural pointer types but they also have potentially more overhead. Variable capture consumes additional resources as does inline declarations. The compiler uses a hidden reference counted interface for inline declarations which adds some minor overhead.

这篇关于方法指针和常规过程不兼容的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆