请教高手,如何把接口作为组件的属性?(100分)

  • 主题发起人 主题发起人 dejoy
  • 开始时间 开始时间
D

dejoy

Unregistered / Unconfirmed
GUEST, unregistred user!
把接口作为属性是可以的:
published
property Explorer : IExplorerIntf read FIExplorerIntf write SetIExplorerIntf;

这样所有实现了IExplorerIntf 接口的组件都可以赋值给Explorer .但是程序销毁时会报错(无法跟踪).

把TComponent作为属性时可以通过重载下面的函数在属性组件移除时得到通知:
procedure Notification(aComponent: TComponent
Operation: TOperation)
override;
可以没有什么函数可以重载以便在接口属性移除时得到通知.
请教高手,该怎么办?
 
看看你的使用例子?
一个容易犯的错误是:class 和 interface 混合使用,导致错误释放对象(如释放两次)
 
应该是重复释放导致的
 
我根本就没有手工释放,我是做成了控件直接放到窗体上的.
 
这个完全是你自己的代码问题.
//可以没有什么函数可以重载以便在接口属性移除时得到通知
SetIExplorerIntf就OK了

调试的方法太多了,推荐的方法,一个窗体,几个BUTTON,代码创建/设置/销毁 跟踪实现接口的类的析构函数.
要注意,如果你的实现接口类是简单类,没有其他额外的生命周期管理,那么没有什么问题,而如果是复杂类,比如TComponent,那么因为TComponent本身有生命周期管理,则需要在SetIExplorerIntf中通知你的实现类,以便在通过其他途径释放实现接口类的时候回调.

总得来讲,你需要系统的看一下 COM/DELPHI_INTERFACE 的生命管理这一块,如果你本身不是很明白的话,可以出错的地方太多,没办法弄的.

type IInterfaceClient_NoRefCount
procedure OnServerDestroy
//begin SetIExplorerIntf(nil)
end;
end;
type IInterfaceServer_NoRefCount
procedure RemoveNotifition(val : IInterfaceClient_NoRefCount);// FClientList.Remove(val);
procedure AddNotifition(val : IInterfaceClient_NoRefCount);//FClientList.Add(val);
end;

procedure TIntfClient.SetIExplorerIntf(NewVal : IExplorerIntf)
begin
if Assigned(FIExplorerIntf)
begin
//*********************
(FIExplorerIntf as IInterfaceServer_NoRefCount).RemoveNotifition(Self.FIExplorerIntf_CallBack as IInterfaceClient_NoRefCount);
//*********************
FIExplorerIntf := nil;
end;
FIExplorerIntf := NewVel;
(FIExplorerIntf as IInterfaceServer_NoRefCount).AddNotifition(Self.FIExplorerIntf_CallBack as IInterfaceClient_NoRefCount);

//使用IInterfaceServer_NoRefCount/IInterfaceClient_NoRefCount并不是太好的方法,一般推荐你使用VCL内部实现的模式,即直接使用一个简单类,这个实现你可以参考TImageList.OnChanged的实现原理
end;

Destruction TIntfClient.Desttructor()
begin
SetIExplorerIntf(nil);
...
inherited;
end;
这样接口客户端的实现就完成了,
Destruction TIntfServer.Destroy;
begin
for aInt:= FClientList.Count-1 downto 0 do
begin
(FClientList[aInt] as IInterfaceClient_NoRefCount).OnServerDestroy();
end;
end;

大致如此,一些席的地方还需要你自己设计,比如可能客户端(实现属性(类型为接口)的类)会有多个接口属性,参考到同一个实现对象,这时候仅仅是如此简单的注册可能就不够用了.

实现较为复杂是因为对于客户端/服务端都没有类型的限制,如果双方有内建机制比如TComponent,那么完全可以改造利用之。

不在多说了,如果你还有疑问,建议 阅读书籍/剖析VCl.
 
兄台真是高人,通过查阅delphi源代码和帮助,发现接口属性移除时也是可以得到通知,贴一段delphi帮助文件中的内容,其原理和 zjan521 兄台的是一样的.
You can use an interface as the value of a published property, much as you can use an object. However, the mechanism by which your component receives notifications from the implementation of that interface differs. In Creating properties for subcomponents, the property setter called the FreeNotification method of the component that was assigned as the property value. This allowed the component to update itself when the component that was the value of the property was freed. When the value of the property is an interface, however, you don't have access to the component that implements that interface. As a result, you can't call its FreeNotification method.

To handle this situation, you can call your component's ReferenceInterface method:

procedure TDemoComponent.SetMyIntfProp(const Value: IMyInterface);
begin
ReferenceInterface(FIntfField, opRemove);
FIntfField := Value;
ReferenceInterface(FIntfField, opInsert);
end;

Calling ReferenceInterface with a specified interface does the same thing as calling another component's FreeNotification method. Thus, after calling ReferenceInterface from the property setter, you can override the Notification method to handle the notifications from the implementor of the interface:

procedure TDemoComponent.Notification(AComponent: TComponent
Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Assigned(MyIntfProp)) and (AComponent.IsImplementorOf(MyInftProp)) then
MyIntfProp := nil;
end;

Note that the Notification code assigns nil to the MyIntfProp property, not to the private field (FIntfField). This ensures that Notification calls the property setter, which calls ReferenceInterface to remove the notification request that was established when the property value was set previously. All assignments to the interface property must be made through the property setter.
 
接口的使用的确不好控制,我通过跟踪发现出错是在system单元的_IntfClear函数中.
我想应该是在程序关闭时重复释放接口引发的错误(是vcl自己释放,我没有释放),并且这个错误只会在程序代码中调用了property Explorer : IExplorerIntf 这个接口属性在关闭时才会出错.
 
呵呵,以前还真不知道有ReferenceInterface这个内建机制.
不过现在手头没有DELPHI,ReferenceInterface应该是TComponent的函数?不知道内部实现代码是什么,我想这很可能是基于双方都是Component的,然后利用接口指针强制转换到TComponent的参考(这个是有内建机制的),然后调用TComponent的通知机制!
但是接口机制的使用其实应改更加广泛,必须是TComponent应该说是一个不小的限制.
 
后退
顶部