代码之家  ›  专栏  ›  技术社区  ›  PA.

如何安全地绕过Delphi错误:“形式参数和实际参数的类型必须相同”

  •  5
  • PA.  · 技术社区  · 15 年前

    我需要一种方法来编写一个通用过程来对一个对象类型或它的任何后代进行操作。

    我的第一次尝试是申报

    procedure TotalDestroy(var obj:TMyObject);
    

    但当它与后代对象一起使用时

    type TMyNewerObject = class(TMyObject);
    var someNewerObject: TMyNewerObject;
    
    TotalDestroy(someNewerObject);
    

    我得到了臭名昭著的错误“形式参数和实际参数的类型必须相同”

    所以,在寻找解决方案时,我研究了delphi系统的源代码 FreeAndNil 程序。我发现这一令人敬畏的声明,以及这一惊人的评论

    { FreeAndNil frees the given TObject instance and 
      sets the variable reference to nil.  
      Be careful to only pass TObjects to this routine. }
    
    procedure FreeAndNil(var Obj);
    

    它避免了类型检查错误,但不使用安全网。

    我的问题是…有没有安全的方法来检查非类型化var参数的类型?

    或者换言之,您能改进这个delphi源代码,这样就不需要警告了吗?

    procedure FreeAndNil(var Obj);
    var
      Temp: TObject;
    begin
      Temp := TObject(Obj);
      Pointer(Obj) := nil;
      Temp.Free;
    end;
    
    3 回复  |  直到 15 年前
        1
  •  7
  •   Community CDub    7 年前

    我以前写过这个,用一个非常类似的例子 Lasse's :

    除非你在给 更改值 对于输入参数本身,而不仅仅是它的一个属性,首先不应该通过引用传递参数。

    如果你 编写一个赋值语句来更改参数的值,那么编译器消息就是真的,您应该注意它。

    需要绕过错误的一个原因是当您正在编写一个类似 TApplication.CreateForm .它的任务是更改输入参数的值,并且新值的类型会发生变化,在编译时无法确定。如果您正在编写这样的函数,那么Delphi的唯一选项是使用 untyped var 参数,然后调用者和接收者都有额外的负担来确保一切正常。调用方需要确保它传递的变量能够保存函数将放入其中的任何类型的值,并且函数需要确保它存储的类型值与调用方请求的类型兼容。

    在情况下 CreateForm 调用方传入类引用文本和该类类型的变量。函数实例化类并将引用存储在变量中。

    我也不怎么看重 创造形式 FreeAndNil 这很大程度上是因为它们的非类型化参数牺牲了类型安全性,而得到的额外便利相对较少。您尚未显示 TotalDestroy 函数,但我怀疑它的var参数最终会提供与其他两个函数相同的低效用。请参阅我的文章:

        2
  •  9
  •   Lasse V. Karlsen    15 年前

    让我们检查一下你想做什么。

    您想调用一个接受x的方法,传入一个类型为y的对象,其中y是x的后代。障碍是,参数是一个“var”参数。

    让我们分析一下如果可能的话你能做什么。

    type
        TBase = class
        end;
        TDescendant = class(TBase)
        end;
    
    procedure Fiddle(var x: TBase);
    begin
        x := TDescendant.Create;
    end;
    
    type
        TOtherDescendant = class(TBase)
        end;
    
    var a: TOtherDescendant;
    a := TOtherDescendant.Create;
    Fiddle(a);
    

    哦,现在 a 不再包含的实例 TOtherDescendant ,它包含的实例 TDescendant . 这可能会让调用后的代码感到惊讶。

    你不仅要考虑你 打算 用你提出的语法,但实际上 能够 使用语法。

    您应该阅读Eric Lippers关于.NET中类似问题的优秀博客文章,网址如下: Why do ref and out parameters not allow type variation? .

        3
  •  3
  •   Mason Wheeler    15 年前

    除了lass写的内容,这是非常正确的,大多数时候你不想把一个对象传递给 var 参数。

    对象是引用类型。你所看到的对象实际上是对它的引用。你只想传递一个对象 参考 到A var 参数,如果要将对象更改为新对象。如果您只想修改对象的成员,那么您可以通过简单地将其传递给一个普通参数来实现这一点。使方法调用 TMyObject 参数而不是 var TMyObject 参数和它应该工作。

    当然,如果你真的要替换这个物体,那么你可以随意地忽略这一切,看看拉瑟的答案。