你能有一个创建类实例的类函数:
TMyClass = class(TSomeParent)
public
class function New(AValue : integer) : TMyClass;
end;
TDerivedClass = class(TMyClass)
public
function Beep;
end;
然后按如下方式使用
...
var
myList : TList<T>;
item : TDerivedClass;
begin
myList.Add(TDerivedClass.New(1))
myList.Add(TDerivedClass.New(3))
myList.Add(TDerivedClass.New(5))
for item in myList do
item.Beep; //times the count in the class function
...
如果是这样,那个功能代码是什么样的?您是否使用TObject的NewInstance方法并且每次为每个派生类重新实现?使用构造函数是否更安全/更好?
目标是在命令模式中使用此方法并使用类类型和接收器加载命令列表,例如:
//FYI: document is an instance of TDocument
commandList.Execute(TOpenDocument(document));
commandList.Execute(TPasteFromClipboard(document));
//... lots of actions - some can undo
commandList.Execute(TPrintDocument(document));
commandList.Execute(TSaveDocument(document));
原因是某些命令将通过text / script指定,需要在运行时解析。
答案 0 :(得分:12)
您正在寻找的是工厂模式。它可以在Delphi中完成;它是VCL如何反序列化形式等等。您缺少的是系统的注册/查找部分。这是基本的想法:
TDictionary<string, TMyClassType>
,其中TMyClassType
定义为class of TMyClass
。这个很重要。您需要在类名和类类型引用之间建立映射。当您需要从脚本中实例化某些内容时,请执行以下操作:
class function TMyClass.New(clsname: string; [other params]): TMyClass;
begin
result := RegistrationTable[clsName].Create(other params);
end;
使用注册表从类名中获取类引用,并在类引用上调用虚拟构造函数以从中获取正确类型的对象。
答案 1 :(得分:5)
是的,技术上可以从类方法创建实例,只需调用实际的构造函数然后返回它创建的实例,例如:
type
TMyClass = class(TSomeParent)
public
constructor Create(AValue : Integer); virtual;
class function New(AValue : integer) : TMyClass;
end;
TDerivedClass = class(TMyClass)
public
constructor Create(AValue : Integer); override;
function Beep;
end;
constructor TMyClass.Create(AValue : Integer);
begin
inherited Create;
...
end;
function TMyClass.New(AValue : integer) : TMyClass;
begin
Result := Create(AValue);
end;
constructor TDerivedClass.Create(AValue : Integer);
begin
inherited Create(AValue);
...
end;
var
myList : TList<TMyClass>;
item : TMyClass;
begin
myList.Add(TDerivedClass.New(1))
myList.Add(TDerivedClass.New(3))
myList.Add(TDerivedClass.New(5))
for item in myList do
TDerivedClass(item).Beep;
在这种情况下,最好直接使用构造函数:
type
TMyClass = class(TSomeParent)
end;
TDerivedClass = class(TMyClass)
public
constructor Create(AValue : Integer);
function Beep;
end;
var
myList : TList<TDerivedClass>;
item : TDerivedClass;
begin
myList.Add(TDerivedClass.Create(1))
myList.Add(TDerivedClass.Create(3))
myList.Add(TDerivedClass.Create(5))
for item in myList do
item.Beep;
答案 2 :(得分:2)
你能有一个创建类实例的类函数吗?
使用构造函数是否更安全/更好?
构造函数是一个创建类实例的类函数。 只是说:
constructor New(); virtual;
你很高兴。
virtual;
部分将允许您为所有后代类调用相同的New()构造函数。
答案 3 :(得分:1)
另一种选择是使用RTTI。下面的代码作为我的类中的常规方法运行,作为使用项子集获取对象的新实例的方法,但由于项(以及列表对象本身)可能是后代对象,因此创建实例定义方法的对象不够好,因为它需要与实例的类型相同。
即
TParentItem = Class
End;
TParentList = Class
Items : TList<TParentItem>;
Function GetSubRange(nStart,nEnd : Integer) : TParentList;
End;
TChildItem = Class(TParentItem)
end
TChildList = Class(TParentList)
end
List := TChildList.Create;
List.LoadData;
SubList := List.GetSubRange(1,3);
如果GetSubRange类似......
Function TParentList.GetSubRange(nStart,nEnd : Integer) : TParentList;
var
aContext: TRttiContext;
aType: TRttiType;
aInsType : TRttiInstanceType;
sDebug : String;
begin
aContext := TRttiContext.Create;
aType := aContext.GetType(self.ClassType);
aInsType := aType.AsInstance;
Result := aInsType.GetMethod('Create').Invoke(aInsType.MetaclassType,[]).AsType<TParentList>;
sDebug := Result.ClassName; // Should be TChildList
// Add the items from the list that make up the subrange.
End;
我很欣赏它可能有点OTT,但在上面的设计中,它有效并且是另一种选择,虽然我很欣赏,但它不是一种类方法。
答案 4 :(得分:0)
您应该使用构造函数(类函数的特殊“类型”)。 TObject.NewInstance
不是合适的选项,除非您需要特殊的内存分配。
关于命令列表的执行例程:现在涉及的操作取决于对象的类型。想象一下,一个文档能够同时打开,打印,粘贴和保存(不是一个奇怪的假设),这在这个结构中很难实现。相反,请考虑添加接口(IOpenDocument,IPasteFromClipboard,IPrintable,ISaveDocument),这些接口确实可以是一个文档实例的操作。