as 到底干什么?

linuxmate 2001-10-17 01:11:06
TMyClass实现了IMyInterface接口,

MyClass: TMyClass;
MyIntf: IMyInterface;

MyIntf := MyClass as IMyInterface;
此命令使用一次正常,以后再用则出错,AS做了些什么呢?

MyIntf := MyClass;
则可以重复使用,这是为什么?

再就是,AS和强制转换有什么不同?仅仅是可以在编译时给提示吗?

--------------------------------------------------------------------

接着往下看,如果我先用 MyIntf := MyClass; 给 MyIntf 赋值,则重复使用

MyIntf := MyClass as IMyInterface;

就没问题了,这又是为什么?

--------------------------------------------------------------------

再往下看,我想把引用的地址显示出来,若用

MyIntf := MyObj as IMyInterface;
edt.Text := IntToStr(Integer(MyIntf));

则可以正常运行一次,而用下面(想省点事)就不行:

edt.Text := IntToStr(Integer(MyObj as IMyInterface));

这又又是为什么?

百思不得其解,请指教!
...全文
87 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
enlightenment 2001-10-17
  • 打赏
  • 举报
回复

超人要睡觉了!:)

enlightenment 2001-10-17
  • 打赏
  • 举报
回复
接口引用计数的规则补充:

4、局部变量规则:属于接口生命周期范围内,无须调用_AddRef及_Release。

5、全局变量规则:对于保存在全局变量中的接口指针,将其传递给另外一个函数之前,必须调用_AddRef。而任何函数都可以调用_Release结束其生命周期。

6、不确定时规则:任何不确定的情形,都应当调用_AddRef和_Release。

Over!

enlightenment 2001-10-17
  • 打赏
  • 举报
回复

这才像个技术问题!


这样来回答吧:

获取接口指针有三种方法,一种是直接分配,一种是as操作符,最后一种——后面讲。

(一)第一种,如(对上面有修改)

MyClass: TMyClass;
MyIntf: IMyInterface;

MyClass:= TMyClass.Create(10); //必须有对象的实例
MyIntf := MyClass as IMyInterface;

但是,有时不知道对象是否支持某接口,容易产生错误,所以一般使用对象的GetInterface函数(该函数是TObject的成员函数),如:

MyClass:= TMyClass.Create(10);
if MyClass.GetInterface(IMyInterface,MyIntf) then
begin
//已经关联,MyIntf可以用了
//该干什么干什么
end;


(二)使用as操作符(注:使用getInterface和as操作符的共同前提是——必须同时赋予一个GUID),如:

MyClass:= TMyClass.Create(10);
MyIntf := MyObj as IMyInterface;

使用as操作符,若对象不支持接口,会产生一个异常。而TObject.GetInterface不会。
另一方面,GetInterface是TObject的成员函数,只能在对象上使用,而as还可以在接口本身中使用,如:

procedure DoSomething(I:IUnkonw);
var
MyIntf:IMyInterface;
begin
(I as IMyInterface).Function; //Function指接口定义的某个函数
end;


而使用as会有一个副作用(也是上面出现问题的关键):使用as操作符,Delphi会调用_AddRef接口使用完成之后会调用_Release函数。
而在使用完as操作符之后,若此时对象的引用计数为0,则该对象将被立刻销毁,如:
begin
MyClass:= TMyClass.Create(10);

DoSomething(MyClass as IMyInterface);//此句执行完成后,对象已经被销毁

MyClass.Function; //此处再调用其方法将出现错误
end;

但如果上面的DoSomething是变参引用或常量引用,则上面引用代码将不会出错,譬如如下面声明:

procedure DoSomething(var I:IUnkonw);

procedure DoSomething(const I:IUnkonw);

因为他们是引用地址传递而不是值传递(引用地址传递,Delphi将不会调用_AddRef、_Release方法)。

(三)绕过自动计数(不推荐使用,因为他不属于规范的代码设计)

就是将上面的对象强制增加引用计数:

begin
MyClass:= TMyClass.Create(10);

MyClass._AddRef; //强制增加调用

DoSomething(MyClass as IMyInterface);//此句执行完成后,对象已经被销毁

MyClass.Function; //此处再调用其方法将出现错误

MyClass.Release; //恢复计数

end;

这样就不用去管DoSomething是如何声明的了!


>>---------------------------------------------------------------

(附注说明)

以上内容看完后,相信你仍然会觉得很不可理解!

你可能会认为:

(问题一)MyClass:= TMyClass.Create(10); //我已经将初始化的引用计数设置为10,怎么还会出现对象被销毁的情况???

这便是Delphi的精妙之处!

他在你的程序中间添加了一层东西——智能指针!使程序员从烦琐、恐怖的构造代码中解放出来。
Delphi中的智能指针是隐含编译的,这也是为什么你的接口引用代码为什么不用象C++中那样显式的调用_AddRef和_Release方法。

不知道你是否对C++的智能指针有所了解?

其实他相当于在接口和实现接口的对象实例中间添加了一层“安全引用计数”,是完全自动的。所以不管你设置对象实例的初始值是多少,都不会起作用!

C++中,Interface被简单定义为等同于Struct的宏,而在我看来,Delphi中的Interface绝不会是那样一个简单的宏定义,因为他至少在编译时包含了智能指针!


(问题二)为什么将接口以值的形式传递给调用函数时可能会引发对象销毁?而使用var及const 不会?

这就是接口规范!(设计规范,就是不要试图去改变的东西)

接口规范包含数个部分,这里列出接口引用计数的规则:

1、输入参数规则:对于传入函数的接口指针,无须调用AddRef和_Release。

2、输出参数规则:任何输出参数中(或返回值)返回一个新的接口指针的函数都必须对此接口指针调用_AddRef,这条规则最经典的例子就是QueryInterface了!

3、输入输出参数:对于输入输出参数传递进来的接口指针,必须在给它赋另外一个接口指针之前调用其_Release方法。在函数返回之前,还必须对输出参数中保存的接口指针调用AddRef。

(我感觉C++的参数与Delphi中有一些微妙变化,自己去Discover吧!)


清晨愉快!

linuxmate 2001-10-17
  • 打赏
  • 举报
回复
阿明高手!

我明白了一些,我先试一下,然后再来发帖。

50分先送上,晚一点再看我的续帖,多谢!
li_zhifu 2001-10-17
  • 打赏
  • 举报
回复
你为什么要用AS?一般说来,就是几个同一类型的控件对应一个共同的事件才用。如:

Button1.OnClick:=ButtonClick;
Button1.Tag:=1;
Button2.OnClick:=ButtonClick;
Button2.Tag:=2;
Button3.OnClick:=ButtonClick;
Button3.Tag:=3;

procedure TFormMain.ButtonClick(Sender: TObject);
begin
ShowMessage('Button'+IntToStr((Sender as TButton).Tag));
end;

5,388

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧