我建立了一个公用的窗体A,B继承自A,在B窗体的某一事件中动态的创建窗体C(modal);我在A窗体中声明了一个公用函数如下,其中的参数setpro和getpro为C窗体的两个赋值过程。我的初衷是让所有继承自A窗体的窗体均可以使用此函数,那样子窗体的代码量就少很多,且好维护。可现在问题是:无法将C窗体的过程传进去,如果硬将C窗体的过程(函数)传进去,就提示我‘实用过程与指针类型冲突’,无法编译。
    请各位高手帮忙解决一下,错在哪儿?或者是告知是不是有其他更好的办法实现我的想法(初衷)。
    谢谢!!
procedure TPublicInfoFm.SaveAll(Fm :TFormClass;Ads :TADODataSet;Setpro :TProcedure;GetPro:TProcedure);
var
  PBfm : Tform;
begin
  try
  //根据窗体状态来判断是新增还是修改
    case self.Frmstate of
      1:
      begin
        //pbfm := fm.Create(application);
        pbfm.Caption := pbfm.Caption+'(新增)';
        if pbfm.ShowModal = mrok then
        begin
          ads.Append;
          Getpro;
          Ads.UpdateBatch();
        end;
      end;
      2:
      begin
        //pbfm := fm.Create(application);
        pbfm.Caption := pbfm.Caption+'(修改)';
        if not Ads.IsEmpty then
        begin
          setpro;
        end else
        exit;
        if pbfm.ShowModal = Mrok then
        begin
          Ads.Edit;
          getpro;
          Ads.UpdateBatch();
        end;
      end;
    end; //case
     pbfm.free;
     pbfm := nil;
  except
      on E:Exception do
      begin
        messagebox(0,pchar(e.Message),'错误',MB_OK+MB_ICONWARNING);
      end;
    end;
end;

解决方案 »

  1.   

    看着好复杂,一点都不简单。偶一般是子窗体的处理函数做成class函数,由父窗体直接调用。
    类似
    class procedure TFrmSystemOptions.CallSystemOptions;
    begin
      if not assigned(FrmSystemOptions) then
        FrmSystemOptions := TFrmSystemOptions.Create(nil);
      try
        FrmSystemOptions.DoSomething;
        FrmSystemOptions.ShowModal;
      finally
        FrmSystemOptions.Free;
        FrmSystemOptions:=nil;
      end;
    end;
      

  2.   

    楼主的意思,是否C是一个接受数据的窗体(新增/修改数据),如果是,用kaguo(▲)Guozhi(★★★★★) 的方法不错, 以C中定义的类方法返回数据(可以用TRecord),然后再在A中根据收到的数据更新数据库……:)
      

  3.   

    我的理解,C是一个输入和显示的窗体,新增和修改是通过B窗体的FRMSTATE来控制的。函数的目的只是为了让子窗体的新增和修改简单一点。
      

  4.   

    嗯,以下是我的建议,你可以参考一下,在C中定义:
    class procedure SetPro(const ADataSet:TADataSet);用于显示数据,在A中从数据库得到数据后,把数据以集合形式传给C,然后C显示;
    class function GetPro(): TADataSet; 用于新增数据,新增数据放在Result里返回;
    再定义一个class procedure EditPro(var ADataSet:TADataSet);用于编辑数据……
    :-)
      

  5.   

    具体不是很清楚
    好像应该这样
    如果你要传窗体的过程的话
    TProcedure = procedure of Object;
    还有你的才c窗口的getpro,setpro如何声明的
    pbfm := fm.Create(application);
    这个为什么注释掉呢
      

  6.   

    我好象明白了楼主的意思,  你只不过是想无认窗口中是新增还是修改,都通过其父类中的SaveAll来处理,  在子窗口中直接调用些函数就可以了!  先不说你遇到的问题,你的这种想法在功能上好象能够实现!  但你想想一点事都没省,弄着一个DataSet对象传过来传过去,还容易出问题!再说,  在你的数据处理窗口中setpro和getpro这两个函数你写不写,当然要写!既然要写为什么不一下写成一个新加函数,一个修改函数!  将这两个函数声明为虚函数,根本不用你传递Fm :TFormClass参数。看来你对面向对象理解不深,尤其是多态!!
      

  7.   

    谢谢各位;
    TO IFindit(寻找其中的乐趣)、Canvas(霜有棱,天无情 - 天下无霜) :
      我水平L,希望两位能不能再具体点。
      盼!
      

  8.   

    TO IFindit(寻找其中的乐趣)、Canvas(霜有棱,天无情 - 天下无霜) :
      在吗?
      

  9.   

    呵呵,ParentForm的方法声明为虚拟的,子窗体中override。当然,也可以不写任何事件。
      

  10.   

    在父类里声明两个虚函数:function NewAddData(para1, para2...): Boolean; Virtual; abstract;
    function ModifyData(para1, para2...): Boolean; Virtual; abstract;
    在子类里声明并定义
    function NewAddData(para1, para2...): Boolean; Override;
    {
    //新增
    }
    function ModifyData(para1, para2...): Boolean; Override;
    {
    //修改
    }
    挺简单的,但是如果你不明白多态的原理,可能也理解不了,赶紧找本面向对象的书,看一下多态,  等程序中第一次用到多态时,心里很爽!
      

  11.   

    先解答楼主的问题,出这个错的原因大概是这样的,你定义参数SetPro和GetPro是TProcedure类型。传入的是C窗口的两个方法对么?但愿我没猜错,如果是这样那问题是出在类的方法和一般的过程并不是一种类型。需要这样定义;
    Type
      MyPro = procedure of object;
    procedure TPublicInfoFm.SaveAll
      (Fm :TFormClass;Ads :TADODataSet;Setpro :TMyPro;GetPro:TMyPro);
    这样应该就可以解决了,前面 wx1452() 已经说过,不知道是不是楼主没有注意到。按照我的理解,这个函数主要就是对外隐藏了保存或者修改的区别,统一都用SaveAll,由A窗口判断来确定是否向C中写入初始值,调用Edit还是Append。不知道是否了解了楼主的意思。那么,在这一套体系中B起到了什么作用呢?为什么我不直接使用A呢?这层继承关系提供了什么方面的灵活性。这是不解一。在你的代码里两句C窗口创建的语句都被注释了,不知道是因为调试不过临时这样作的,还是根本不要这两句,如果不是在这里创建的,那么是在那里创建的呢。此外如果是已经创建好了C在调用这个方法,那么参数不应该是TFormClass。这是不解二。如果SetPro和GetPro都是C的方法,那为什么不这样调用:
      pbfm.SetPro;
    而要采用复杂的函数指针的方式?这是不解三。这个倒有一个可能,因为从TFormClass的实例是没办法调用到你要在C窗口中定义的方法的,那么其实你可以这样:
    Type
      TCFormClass = class of TCForm;
    参数中传递TCFormClass类型就可以了。你是想可以统一处理各种窗口,那么这些窗口都从CForm中继承。其实整个体系需要特化的正是C窗口而不是你原来想的A窗口。我看到你传入的两个函数都是无参数的,也就是说你是通过其他方式来获取过程需要的输入和保存最终的输出,也就是DataSet中的数据。(暂时不讨论耦合的问题)。那么为什么不这么写呢。
    procedure TCForm.NewRecord;
    begin
      FDataSet.Append;
      ...
      FDataSet.UpdateBatch;
    end;procedure TCForm.EditRecord
    begin
      FDataSet.Edit;
      ...
      FDataSet.UpdateBatch;
    end;
    在A中直接调用C的两个函数即可,也不必传这么一大堆东西了,这是不解四。下面就是一些空泛道理了,可能一下用不上,不过我觉得你了解一些应该会比较好。
    1,不要在一个函数里用一个标志变量分成不相干的两支。不知道你有没有学过高内聚低耦合的知识,如果没有或者淡忘了可以看看,这种情况是非常典型的低内聚错误。把它分成两个函数,删除那个标志变量。
    2,不要用标志变量,如果一定要用把它定义为有意义的枚举类型。如
    type
      TFormState = (fsNew, fsModify);
    FormState: TFormState
    不过就算如此,还是不如干脆没有。每个标志变量都象是头朝上放在凳子上的图钉。等你放多了你就会明白它会怎么伤害你了。
    3,其实你这么复杂的搞了一套,不过就是为了在每个弹出窗口中省去一句判断调用Apped还是Edit而已。值得么?如果真的这个方面重复工作这么多,应该考虑统一用一个窗口完成所有数据的新增和修改,窗口根据数据内容动态生成界面。程序构架应该表现问题的结构而不是你的实现的结构。
    4,界面其实没有必要作这么多工作,应该是单纯的作显示和交互的任务即可。在界面显示的时候从后台读出需要的数据,接收了输入后调用后台对应的对象。真正体现问题解决方式的应该是业务逻辑层的对象。
      

  12.   

    真心的感谢各位的解答!!
    TO asj() :
      不解一:B是真正的显示窗体,之所以用到A是因为,除了B之外还有数个跟B一样,继承自A的窗体。
      不解二:你猜测的没错,我确是在没编译过时,修改时暂注释掉,放上来忘了修正,SORRY。
      不解三:asj()兄的意思是类方法?
      不解四:A其实没具体实现,只不过做为父窗体存在。
    其实,我也觉得可能走了弯路,但我的水平还真不知如何纠正。。
      该如何??
      问题解决后,我愿再开贴答谢。
      

  13.   

    .....
    你回答的什么啊?
    1,我知道B是从A继承的,问题是你是出于什么考虑要让这几个窗口从A继承,我丝毫没有看出来。只是用这个函数的话就没必要了,要是每个窗口都用到stringlist难道还要都从stringlist中继承?
    2,...
    3,和类方法有什么关系啊?比如你生成了一个Form2,上面放了一个Edit1。你告诉我你怎么从里面读出来内容啊。
      Form2.Edit1;
    那如果是Form2,你在里面写了一个公有的函数Proce1,你再来告诉我怎么调用好么?
    4,和A有没有具体实现有什么关系?你原来的方案没有具体实现你不是一样写了这么一大堆了,现在只是让你把那些东西都写到C里面去,A里只调用两个方法。现在有个更大的不解,你现在的问题是什么?如果是你最初提出来的那个,应该改了参数类型就OK了。如果你是想知道怎么更好的解决,我的建议是,不要搞这一套东西,除了增加你犯错的机会没什么好处。我真的不想说,不过还是得告诉你,你在基础方面差的实在是太多,像你这样的如果和我一起,恐怕我要用你写代码相等的时间来改它。不过看你的代码,如果都是你写的,那也不是完全不会编码的人写的,不知道是不是开始就是从别人的相似代码中改改拼拼中摸索过来的。如果是这样,建议你系统的学习一下Delphi编程,不要只是拉几个控件,拼个数据库程序就算完工了。否则很多东西你的假定和基本的思路都是错的自己还没有意识,这种问题别人很难一句一句给你很快讲明白的。建议你看看《Delphi5开发人员指南》