为什么Delphi编译器无法识别我正在尝试释放一个接口?

5

这个周末我在编码时犯了一个小错误。
在以下代码中,我创建了一个对象并将其转换为一个接口。稍后,我试图使用FreeAndNil()释放它;

type
  IMyIntf = interface
     [...]
  end;

  TMyClass = class(TInterfacedObject, IMyIntf)
     [...]
  end;

var
  Myintf : IMyIntf;
begin
  Myintf := TMyClass.Create;
  [...] // Some process
  FreeAndNil(Myintf); // CRASH !!!
end;

当然,程序在这一行崩溃了。我完全理解问题所在,但是我不明白的是,为什么编译器不会警告我呢?没有动态的东西在里面,我只是试图释放一个接口!!! 为什么它不写出错误/警告呢?

到底有没有真正的解释,还是只是编译器的限制?


1
计算机只会按照你告诉它们该做什么去做,而不是你想要它们做什么。 - Seth Carnegie
@Seth 当我在编写GET_THE_THINGS_DONE()时,它并没有按照我的要求去执行... :) - TridenT
实际上,崩溃发生在您所述的下一行。无论如何,编译器看不见,因为它没有眼睛。http://ecx.images-amazon.com/images/I/51QR-B9n6lL._SL500_AA300_.jpg - Premature Optimization
@TridenT 我敢打赌它会调用 GET_THE_THINGS_DONE,因为这就是你告诉它要做的事情。 - Seth Carnegie
1个回答

10

你知道,正确的做法是写Myintf := nil,或者让它超出作用域。你问的问题是为什么编译器会接受FreeAndNil(Myintf)而不在编译时抱怨。

FreeAndNil的声明是:

procedure FreeAndNil(var Obj);

这是一个未指定类型的参数,因此它可以接受任何类型的值。你传递了一个接口作为参数,但也可以传递一个整数、字符串等。

为什么设计者选择使用未指定类型的参数呢?因为他们需要使用 var 参数,由于 FreeAndNil 的目的是释放对象并将对象引用设置为 nil,所以这不能通过目标对象的方法实现,必须使用独立函数的 var 参数来实现。

你可能会想到可以编写以下代码:

procedure FreeAndNil(var Obj: TObject);

因为所有的对象都是从 TObject 继承而来。但这并不能解决问题。原因在于,你传递给 var 参数的对象必须完全匹配该参数的类型。如果 FreeAndNil 被声明为这种方式,那么每次调用它时都必须强制转换为 TObject

因此,设计者们决定最好的解决方案是使用无类型 var 参数,这是最不糟糕的选择。


2
@TridenT 那是正确的。为了能够完成我的日常工作,我写了一个名为 HeffBot 的机器人来替我处理 Stack Overflow 的问题... ;-) - David Heffernan
1
你可以认为更好的解决方案是修复语言,使某个对象类型的“var”参数接受对该类型或任何从它派生的类型的引用。我模糊地记得有人曾经试图解释这将是一个糟糕的事情,但现在我无法理解其合理性。 :) - Deltics
1
如果您被允许传递一个被声明为变量参数类型的后代类型的变量,那么您可能会分配错误类型的实例!请考虑以下示例:type TFoo = class(TObject) end; TBar = class(TObject) end;procedure SetInstance(var Inst: TObject); begin Inst := TFoo.Create; end;var O: TBar; begin SetInstance(O); end. - Allen Bauer
1
即使添加协变/逆变性也无法帮助全局函数,因为普通的全局函数/过程无法成为泛型。只有结构类型(类、记录、接口)及其上的方法可以是泛型。可以向TObject添加新的类静态方法来完成相同的操作,但这不会帮助现有的函数。 - Allen Bauer
1
David,这已经是一个SO问题了(更不用说提到FreeAndNil了):如何安全地绕过Delphi错误:“形式和实际参数的类型必须相同”。此外,还有另一个链接给@Deltics,就是我五年前写的文章:为什么实际和形式变量参数的类型必须相同? - Rob Kennedy
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接