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

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

问题描述

我有一个应用程序,它有多种形式。所有这些形式都有一个PopupMenu。我以编程方式构建菜单项,全部在常用的根菜单项下。我想要所有的菜单项调用相同的过程,菜单项本身基本上是作为一个参数....



我有这个工作,当我刚刚一个形式做这个功能。我现在有多种形式需要这样做。我将所有的代码移动到一个共同的单元。

 示例。 
Form A有PopupMenu 1.单击时,调用Unit CommonUnit中的代码。
表单B有PopupMenu 2.单击时,调用CommonUnit单元中的代码。

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



我正在使用代码将项目添加到我的PopupMenu 。

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

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



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



I当我在一个单一的表单上这样做时,已经定义了BrowseCategories1Click,就像以前一样。唯一的区别是它现在被定义在一个共同的单位,而不是一个表单的一部分。



它被定义为

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

最简单的方法是什么?



感谢
GS

解决方案

一点背景...



Delphi有3种程序类型:




  • 独立版或单位范围函数/程序指针如下所示:



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


  • 方法指针如下声明:



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


  • 而且,由于Delphi 2009,匿名(见下文)这样声明的函数/方法指针:



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




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



所以你的事件处理程序必须被定义为一些类定义的一部分,任何类定义来安抚编译器。



由于TOndrej建议您可以修改编译器,但是如果这些事件处理程序在同一个单元中,那么它们应该已经相关了,所以你可以继续将它们包装到一个类中。 >

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

  TForm1.BrowseCategoriesClick (发件人:TObject)
begin
BrowseCategories;
结束

TForm2.BrowseCategoriesClick(发件人:TObject)
begin
BrowseCategories;
结束






  CommonUnit 

界面
程序BrowseCategories;
begin
//
end;

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



您选择的方向最终取决于您,但我会请小心您将重点放在哪个选项将使得可维护性在未来更容易,而不是现在最方便的。





匿名方法



匿名方法是一个不同的野兽。匿名方法指针可以指向独立函数,方法或内联声明的未命名函数。最后一个函数类型是从中获取匿名的名称。匿名函数/方法具有捕获在范围之外声明的变量的独特能力

  function DoFunc(Func:TFunc< string>) :string 
begin
结果:= Func('Foo');
结束

//其他地方
程序CallDoFunc;
var
MyString:string;
begin
MyString:='Bar';
DoFunc(function(Arg1:string):string
begin
结果:= Arg1 + MyString;
end);
结束

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


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.

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.

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);

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

Incompatible types: 'method pointer and regular procedure'.

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.

It is defined as

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

What is the easiest way to get around this?

Thanks GS

解决方案

A little background...

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 pointers declared like so:

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

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

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

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.

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

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 methods

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天全站免登陆