在只知道基类类名的情况下如何复制子类的实例?(200分)

  • 主题发起人 主题发起人 5rain6sky
  • 开始时间 开始时间
to 5rain6sky:
也许我还没有理解您的意思,不过还是请您看看:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=640050
我通过一个超级基类实现了父类访问子类中的方法。(其实还可以更简单的——用{$M+}
进行方法与方法名的自动绑定。 ^_^ )

注意到在您的应用中,仅仅需要访问子类的一两个固定的方法,没有必要像我的程序那么
复杂,完全可以将这两个方法的入口指针放到基类中去,这样速度也不会有什么损失,只要
在子类Create的时候将这两个方法指针指向自己的处理过程就可以了。

eg:
type
TBase=class
public
DoMain:function (Str:String):Integer of Object;
DoElse:procedure (Str:String) of Object;
end;
TA=class(TBase)
private
Count:Integer;
function _DoMain(Str:String):Integer;
procedure _DoElse(Str:String);
public
constructor Create
//当然,如果有必要,还要重载Destroy方法
end;

procedure TForm1.Button1Click(Sender: TObject);
var
A:TA;
Base:TBase;
begin
A:=TA.Create;
Base:=A
//在这个例子中,无所谓什么Clone不Clone了——你既然已经得到了子类的实例
//就完全可以调用DoMain和DoElse方法,不存在什么能不能的问题。
Base.DoMain('asdf');
Base.DoElse('3453');
Base.Free;
end;

{ TA }

constructor TA.Create;
begin
inherited;
DoMain:=_DoMain;
DoElse:=_DoElse;
Count:=0;
end;
procedure TA._DoElse(Str: String);
begin
ShowMessage(IntToStr(Count)+' '+Str);
end;
function TA._DoMain(Str: String): Integer;
begin
Inc(Count);
ShowMessage(Str);
end;
 
>>因为那个B我还要在调用者程序里接着用,我不想在被调用程序里改变它的任何属性
哈哈!原来是这么回事呀!
你的要求让我想起了以前看到过的一篇文章(DOS时代的)——有人想在TSR中调用某个系统
过程(大概是这么回事吧 *_^ ),但是调用该过程的必然结果是——调用环境的许多环境参数
会被该过程破坏掉,比如鼠标中断入口等等。别人的作法是——既然调用它得不偿失,还是
自己写吧。他是这么解决的——既然你会破坏,那我就在调用之前将所有可能被破坏的东西先
保存起来,然后让你破坏,完了以后再自己恢复到原来的样子。——办法不错吧!
按照这个思想,您只要在基类中写几个诸如SaveObjectImageToMem、LoadObjectImageFromMem
的方法,实现子类的状态保存和恢复就可以了,不用担心因为要使用Copy出来的对象可能造成的
非法访问。
我还是不太理解——“先用子类的一个实例做一些判断工作,如果该子类可以处理...”
——既然您的判断是基于某个子类,而不是这个子类的某个特定实例,完全可以采用 类方法 以
实现您的“判断工作”,这样不就可以不用“实例”了吗??
 
to All:各位,不好意思,最近忙得要死,好久没来。
对此问题,这段时间我采用了回避的办法,还是没有彻底解决,所以还请各位出出主意。

to flier:
恕我愚钝,我试了你给出的方法,似乎只能复制同类,
因为它要求 as XXXX,但我的问题正在于这个XXXX无从得知。
请再次指教!

to creation-zy:
我虽然可以用类方法做判断,但我同样要用子类的实例做实际的处理工作,
所以关键问题还是要先在线程中创建出正确的子类实例来才行。
 
to 5rain6sky:
>>创建出正确的子类实例
好像并不困难吧...
请您仔细看看我给出的帖子,我在那里已经给出了一个创建并调用DLL中的未知子类的完整解决方案。
也许我的例子还不十分有说服力,比如子类还没有增加新的数据成员。为了增强说服力,我可以进行
修改:

在 TWorldSub_A 的声明中增加数据成员 Number 和 Buf:
TWorldSub_A=class(TWorldBase)
Number:Integer;
Buf:array[0..12]of Byte;
procedure DoProc(ProcName:ShortString)
override;
procedure Init
override;
procedure DoA;
procedure DoAA;
end;
相应的,在它的 DoA 过程中增加访问子类特有成员的语句:
procedure TWorldSub_A.DoA;
begin
Number:=(Number+1) mod SizeOf(Buf);
Buf[Number]:=Number;
ShowMessage('Hello!'+IntToStr(Number));
end;

编译DLL,运行Caller.exe,调用CallDll.dll中 TWorldSub_A 的 DoA 方法——没有任何问题!

子类的生成过程是在DLL中实现的:
function NewObject(Name:ShortString):TWorldBase;stdcall;
var
n:Integer;
begin
Result:=nil;
n:=ClassNames.IndexOf(Name);
if n>=0 then
begin
TObject(Result):=TClassInfoStore(ClassNames.Objects[n]).CInfo.Create;
Result.Init;
end;
end;

完整的源代码我的主页上有(见个人信息)。
 
to creation-zy:
你的例子里与我的问题关系最密切的就是NewObject,而你的NewObject方法并不能提供实例的复制,
事实上你从外部调用DLL的时候没有传过来任何实例的内容信息,所以与我的要求不符。
Anyway,还是要感谢你的热心帮助![:)]

to flier:
在你提供的代码的启发之下,我终于把它搞定了!
多问一句,你给出的这种方法是不是类似于(或者就是)《设计模式》里讲的Singleton模式的应用?

to All:
我原来为基类和每个子类写过一个Assign方法来复制实例的内容,类似这样——
procedure TBase.Assign(AObj: TObject);
begin
if AObj = nil then Exit;
if not (AObj is TBase) then Exit;
with TBase(AObj) do begin
Clear;//清除、初始化
inherited;
Request.Assign(Self.Request);//Request是TBase的一个属性(也是一个TBase对象)
end;
end;
——唉,其实就差这么一步,我怎么就没想到既然这样做可以让子类各自复制出正确的实例内容,
那也就可以创建各自的实例本身!因为它自己当然知道自己是什么类的:)

flier给出的这段代码给我了关键的启示——
function TNoBitwiseCopy.Clone: TInterfacedObject;
begin
Result := TNoBitwiseCopy.Create(GetName);
end;
因为我没有接触过接口的编码,一开始我在研究他给的接口应该怎么写,不过到现在也没弄明白:)
好在忙过了这一阵,轻松下来脑子也转开了,才发现根本没必要用接口,
写个这样的Clone函数就OK了——
function TDatas.Clone: TBase;//TDatas是TBase的一个子类
begin
Result := TDatas.Create;
Assign(Result);
end;
这个Clone函数在TBase里以纯虚方法出现即可。
这样,将Clone和Assign配合起来就完成了任意子类实例的创建和复制的过程。

我已经测试过了,大家看还有没有什么可以改进的,欢迎继续讨论。
如果大家都觉得我的方法没什么问题,我最迟本周末结帐。
 
多谢各位!
 

Similar threads

S
回复
0
查看
896
SUNSTONE的Delphi笔记
S
S
回复
0
查看
873
SUNSTONE的Delphi笔记
S
S
回复
0
查看
1K
SUNSTONE的Delphi笔记
S
后退
顶部