我可以创建一个构造函数来反序列化对象的字符串版本吗?

发布于 2024-10-12 14:30:58 字数 801 浏览 5 评论 0原文

我正在使用 Delphi 帮助文件中 ComponentToString 部分中的示例序列化和反序列化一个对象(TComponent 后代)。这样我就可以将对象存储在数据库的 VARCHAR 字段中。

当我需要从数据库中存储的字符串实例化类的新实例时,我可以使用 CreateFromString(AOwner: TComponent; AData: String) 形式的构造函数来实现吗?或者我是否必须使用返回组件类实例的非类方法?

如果我可以使用构造函数版本,如何将 ReadComponent 的返回值“映射”到构造函数创建的“self”?

这是帮助文件中的反序列化示例:

function StringToComponentProc(Value: string): TComponent;
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Seek(0, soFromBeginning);
      Result:= BinStream.ReadComponent(nil);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

I'm serializing and deserializing an object (TComponent descendant) using the example in the ComponentToString section in the Delphi help file. This is so I can store the object in a VARCHAR field in the database.

When I need to instantiate a new instance of my class from a string stored in the database, can I do that using a constructor of the form CreateFromString(AOwner: TComponent; AData: String)? Or do I have to use a non-class method that returns an instance of my component class?

If I can use the constructor version, how to I "map" the return value of ReadComponent to the "self" that is being created by the constructor?

Here's the deserialization example from the help file:

function StringToComponentProc(Value: string): TComponent;
var
  StrStream:TStringStream;
  BinStream: TMemoryStream;
begin
  StrStream := TStringStream.Create(Value);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Seek(0, soFromBeginning);
      Result:= BinStream.ReadComponent(nil);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

匿名的好友 2024-10-19 14:30:58

一般来说,是的,您可以使构造函数反序列化字符串并使用该信息来初始化新实例。一个简单的例子是具有单个 Integer 字段的类。将字符串传递给构造函数,让构造函数调用 StrToInt 并使用结果初始化字段。

但是,如果您用于反序列化的唯一函数也是创建实例的函数,那么您就不能从构造函数中使用该函数,因为当您只需要一个实例时,您最终会得到两个实例。构造函数不可能说:“没关系;毕竟不要构造实例。我已经在其他地方得到了一个实例。”

但是,这不是您所处的情况。您应该知道, TStream.ReadComponent 允许您自己创建实例。仅当您尚未为其提供要使用的实例时,它才会实例化该类。您应该能够像这样编写构造函数:

constructor TLarryComponent.CreateFromString(const AData: string);
var
  StrStream, BinStream: TStream;
begin
  Create(nil);
  StrStream := TStringStream.Create(AData);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Position := 0;
      BinStream.ReadComponent(Self);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

我们将由 Self 指定的当前对象传递给 ReadComponent。流将忽略存储在流中的类名,并假定当前对象属于正确的类。

In general, yes, you can make a constructor deserialize a string and use that information to initialize the new instance. A trivial example of that would be a class with a single Integer field. Pass a string to the constructor and have the constructor call StrToInt and initialize the field with the result.

But if the only function you have for deserialization is one that also creates the instance, then you cannot use that from the constructor because then you'll end up with two instances when you only wanted one. There's no way for a constructor to say, "Never mind; don't construct an instance after all. I already got one somewhere else."

However, that's not the situation you're in. As you should know, TStream.ReadComponent allows you to create the instance yourself. It only instantiates the class if you haven't already given it an instance to use. You should be able to write your constructor like this:

constructor TLarryComponent.CreateFromString(const AData: string);
var
  StrStream, BinStream: TStream;
begin
  Create(nil);
  StrStream := TStringStream.Create(AData);
  try
    BinStream := TMemoryStream.Create;
    try
      ObjectTextToBinary(StrStream, BinStream);
      BinStream.Position := 0;
      BinStream.ReadComponent(Self);
    finally
      BinStream.Free;
    end;
  finally
    StrStream.Free;
  end;
end;

There we're passing the current object, designated by Self, to ReadComponent. The stream will ignore the class name stored in the stream and assume that the current object is of the correct class.

抽个烟儿 2024-10-19 14:30:58

您可以通过(静态)方法来完成此操作,但不能通过构造函数来完成此操作。
Delphis 的构造函数由编译器内在刚刚创建的实例上调用,该实例已经部分初始化(它是所需的类,并且实例/字段存储已清零)。

如果你查看TStream.ReadComponent的源码,你会发现组件的真实类首先从源流中读取,然后构造一个空实例并由RTTI从流中填充,然后作为结果返回。这意味着:

要使用TStream.ReadComponent,您需要通过RegisterClass将您的类注册到Delphis的流系统。

You can do this by a class (static) method, but not via a constructor.
Delphis' constructors are called by compiler intrinsic on the just-created instance, which is already partially initialized (it's of the desired class and instance/field storage is zeroed-out).

If you see the source of TStream.ReadComponent, you'll find that the components' real class is read from the source stream at first, then an empty instance is constructed and filled by RTTI from the stream and returned as the result. Which means:

To use TStream.ReadComponent, you'll need to register your class to Delphis' streaming system via RegisterClass.

甜心 2024-10-19 14:30:58

使用静态类函数而不是构造函数

type
  TYourClass = class(TComponent)
  public
    class function CreateFromString(AOwner: TComponent; AData: String): TYourClass; static;
  end;

implementation

class function TYourClass.CreateFromString(AOwner: TComponent; AData: String): TYourClass;
begin
   Result := (StringToComponentProc(AData) as TYourClass);
   if AOwner <> nil then
     AOwner.InsertComponent(Result);
end;

AOwner部分可能是个问题,因为TStream.ReadComponent没有所有者的参数。

关于该问题还有另一个问题:

如何指定从 Delphi TStream 读取的组件的所有者?

编辑: 我已经更新了代码示例以包含所有者。

请注意,插入到所有者的组件列表中需要为要插入的组件提供唯一的或空的Name

Use a static class function instead of a constructor:

type
  TYourClass = class(TComponent)
  public
    class function CreateFromString(AOwner: TComponent; AData: String): TYourClass; static;
  end;

implementation

class function TYourClass.CreateFromString(AOwner: TComponent; AData: String): TYourClass;
begin
   Result := (StringToComponentProc(AData) as TYourClass);
   if AOwner <> nil then
     AOwner.InsertComponent(Result);
end;

The AOwner part could be a problem though, since TStream.ReadComponent has no parameter for the owner.

There is another so question about that problem:

How can I specify the Owner of component read from a Delphi TStream?

Edit: I've updated the code sample to include the owner, too.

Note that inserting into the component list of the owner requires a unique or empty Name for the component that is being inserted.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文