If you declare a variable of an
interface type, the variable can reference instances of any class that
implements the interface. Such variables allow you to call interface methods
without knowing at compile time where the interface is implemented. But they
are subject to the following limitations.
· An interface-type expression gives you access only to methods and
properties declared in the interface, not to other members of the implementing
class.
· An interface-type expression cannot reference an object whose class implements
a descendant interface, unless the class (or one that it inherits from)
explicitly implements the ancestor interface as well.
For example,
type
IAncestor = interface
end;
IDescendant = interface(IAncestor)
procedure P1;
end;
TSomething = class(TInterfacedObject, IDescendant)
procedure P1;
procedure P2;
end;
...
var
D: IDescendant;
A: IAncestor;
begin
D := TSomething.Create; //
works!
A := TSomething.Create; //
error
D.P1; // works!
D.P2; // error
end;
In this example,
· A is declared as a variable of type IAncestor. Because TSomething
does not list IAncestor among the interfaces it implements, a TSomething
instance cannot be assigned to A. But if we changed TSomething's
declaration to
TSomething = class(TInterfacedObject,
IAncestor, IDescendant)
...
the first error would become a
valid assignment.
· D is declared as a variable of type IDescendant. While D
references an instance of TSomething, we cannot use it to access TSomething’s
P2 method, since P2 is not a method of IDescendant. But if we changed D’s
declaration to
D: TSomething;
the second error would become a valid method call.
Interface references are managed
through reference-counting, which depends on the _AddRef and _Release
methods inherited from IInterface. When an object is referenced only
through interfaces, there is no need to destroy it manually; the object is
automatically destroyed when the last reference to it goes out of scope.
Global interface-type variables
can be initialized only to nil.
To determine whether an
interface-type expression references an object, pass it to the standard
function Assigned.
Interface
assignment-compatibility
如果声明了一个接口类型的变量,那么该变量可以引用任何实现了该接口的类的实例。这样的变量允许调用接口方法而不需要知道编译时哪一个接口被实现了。但它们受到下列约束:
· 接口类型的表达式仅给出了对接口中声明的方法的访问,而不是对实现接口的类中的其他成员。
· 接口类型的表达式不能引用实现后裔接口的类的对象,除非该类或其祖先类也实现了祖先接口。
例如,
type
IAncestor = interface
end;
IDescendant = interface(IAncestor)
procedure P1;
end;
TSomething = class(TInterfacedObject, IDescendant)
procedure P1;
procedure P2;
end;
...
var
D: IDescendant;
A: IAncestor;
begin
D := TSomething.Create; // 正确运行
A := TSomething.Create; // 错误一
D.P1; // 正确运行
D.P2; // 错误二
end;
在上面的例子中,
· A被声明为类型为IAncestor的变量。因为TSomething不能在其实现的接口中列出,所以TSomething实例不能被赋值给A。但如果将TSomething的声明改为
TSomething = class(TInterfacedObject,
IAncestor, IDescendant)
...
那么错误一将成为有效的赋值语句。
· D被声明为类型为IDescendant的变量。当D引用TSomething的实例时,不能用其访问TSomething的P2方法,因为P2不是IDescendant的方法。但如果将D的声明改为
D: TSomething;
那么错误二将成为有效的方法调用。
接口引用通过引用计数来管理,引用计数基于自IInterface继承的_AddRef和_Release方法。当一个对象仅通过接口被引用时,不需要对其手工销毁;当最后的引用离开作用域时,该对象被自动销毁。
接口类型的全局变量只能被初始化为nil。
要确定一个接口类型表达式是否是引用了一个对象,可以将其传递给标准函数Assigned。