简体   繁体   English

在Delphi中创建和销毁对象

[英]Creating and destroying objects in Delphi

I am new to Delphi and am trying to better understand object creation / freeing as I am used to the luxury of .NET's GC. 我是Delphi的新手,并且由于习惯于.NET GC的使用,我试图更好地理解对象创建/释放。 I have two questions specifically: 我有两个具体问题:

  1. Let's assume I am setting a TDataSource as below. 假设我正在如下设置TDataSource。 In .NET I wouldn't explicitly destroy the object as I am with adoQuery.Free. 在.NET中,我不会像使用adoQuery.Free那样显式销毁对象。 But I am assuming that with Delphi I need to free these objects. 但是我假设使用Delphi需要释放这些对象。 However, by destroying the adoQuery I am also setting the dataset to null. 但是,通过销毁adoQuery,我还将数据集设置为null。 In this way adoQuery is meant to be a locally scoped variable to the function only with the datsource being retuned from the function. 这样,adoQuery仅在从该函数重新调整datsource时才是该函数的局部范围变量。 Therefore, how can I best handle this? 因此,我该如何最好地处理呢?

     dataSrc := TDataSource.Create(nil); dataSrc.DataSet := adoQuery; dataSrc.Enabled := true; { adoQuery.Free; } cnt := DataSrc.DataSet.RecordCount; 
  2. I've been reading several suggestions when returning a variable from a function that the best thing to do is create the variable within the caller and pass it to the subroutine. 从函数返回变量时,我一直在阅读一些建议,最好的办法是在调用方中创建变量并将其传递给子例程。 Therefore, the signature to a function would look like: 因此,对函数的签名如下所示:

     AdoConnectionManager.GetResult(query : String; dataSrc: TDataSource) : TDataSource; Result := dataSrc; 

This is unattractive to me. 这对我来说没有吸引力。 I'd prefer to have a new variable created within the subroutine and then returned back to the caller. 我希望在子例程中创建一个新变量,然后将其返回给调用者。 However, this is something again I never really had to worry about with .NET GC and here I have to explicitly destroy the variable, right? 但是,这是我再也不用用.NET GC担心的事情了,在这里我必须显式销毁变量,对吗?

Thanks! 谢谢!

You've asked two questions. 您问了两个问题。 One concerns these database classes, and I'm going to ignore that question since I don't know anything about those classes. 一个与这些数据库类有关,由于我对这些类一无所知,因此我将忽略该问题。 Instead I will answer the other questions. 相反,我将回答其他问题。 Do note that this sort of answer is why the site policy is for questions to be asked one at a time. 请注意,这种答案就是为什么网站政策一次只问一个问题。

Regarding a function that returns a new object, that is certainly viable. 对于返回新对象的函数,这当然是可行的。 However, it is sometimes more flexible to let the caller supply the object. 但是,有时让调用者提供对象更加灵活。 That allows them to re-use instance, or supply objects that are derived from a base class. 这样一来,他们就可以重用实例,或提供从基类派生的对象。 A classic example would be a function that populated a TStrings instance. 一个经典的例子是填充TStrings实例的函数。

In this scenario you'd probably use a procedure rather than a function. 在这种情况下,您可能会使用过程而不是函数。 It might look like this: 它可能看起来像这样:

procedure PopulateList(List: TMyList);

If you want to have a function that returns a newly minted instance that would be done like so: 如果您希望有一个函数返回一个新创建的实例,则可以这样进行:

function CreateAndPopulateList: TMyList;
begin
  Result := TMyList.Create;
  try
    // code to populate Result goes here, and may raise exceptions
  except
    Result.Free; // in case of exceptions, we must destroy the instance to avoid leaks
    raise;
  end;
end;

Note the naming. 注意命名。 I use create to imply to the caller that a new instance is created. 我使用create来暗示调用者已创建一个新实例。 The calling code would look like this: 调用代码如下所示:

List := CreateAndPopulateList;
try
  // do stuff with list
finally
  List.Free;
end;

And this pattern is the standard object creation pattern. 这种模式是标准的对象创建模式。 So you use CreateAndPopulateList just as you could a constructor. 因此,您可以像构造函数一样使用CreateAndPopulateList

It should also be mentioned here, that Delphi also provides Reference-Counting (but different to .NET). 这里还应该提到,Delphi还提供了引用计数(但与.NET不同)。

The very short explanation to Reference-Counting in Delphi: 对Delphi中引用计数的简短说明:
In difference to other Languagues, Reference-Counting in Delphi is only available by using Interfaces. 与其他语言不同,Delphi中的引用计数只能通过使用接口来使用。 Further, there is no Garbage-Collector: a reference-counted Object gets instantly destroyed when its Referencecount reaches 0. 此外,没有垃圾收集器:一个引用计数的对象在其引用计数达到0时立即被销毁。

So as an Delphi Developer, there are the following "global" Rules for destroying Instances: 因此,作为Delphi Developer,有以下销毁实例的“全局”规则:
- you do destroy an Object manually, whenever it's declared as a ClassType (eg var m: TMyClass) - 只要将对象声明为ClassType即可手动销毁它(例如var m:TMyClass)
- you never destroy an Object manually, whenever it's declared as a InterfaceType (eg var m: IMyClass) -无论何时将对象声明为InterfaceType(例如var m:IMyClass), 您都不会手动销毁

With Delphi when you create an object, you should decide how it would be freed. 使用Delphi创建对象时,应决定如何释放它。 There are several ways: 有几种方法:

  1. You can free it manually 您可以手动释放它
  2. It can be freed together with it's owner 可以与其所有者一起释放
  3. It can be freed as a part of TObjectList or similar container 可以将其作为TObjectList或类似容器的一部分释放
  4. It can be freed because it's interfaced and reference counter for it became zero 可以释放它,因为它已连接并且其引用计数器变为零
  5. And so on... 等等...

About first question: you should understand that with Delphi object variable is a pointer to object. 关于第一个问题:您应该了解,使用Delphi的对象变量是指向对象的指针。 When leaving function, you can lost locally scoped (pointer) variable, but you don't harm object itself. 离开函数时,可以丢失局部作用域(指针)变量,但不会损害对象本身。 For example, you can do something like this: 例如,您可以执行以下操作:

function GetDataSource: TDataSource;
var Query: TADOQuery;
begin
  Result := TDataSource.Create(nil);
  Query := TADOQuery.Create(Result);
  Query.SQL.Text := ' ... ';
  Result.DataSet := Query;
end;

It'll give you datasource you want with background query. 它将通过后台查询为您提供所需的数据源。 When you free this datasource, query also would be freed. 当您释放此数据源时,查询也将被释放。

About second question: intrafunction creation of return object is a normal practise, part of good design. 关于第二个问题:返回对象的功能内创建是一种正常做法,是良好设计的一部分。 Yes, you should decide who will free this object and how. 是的,您应该决定谁将释放该对象以及如何释放。 You can use many strategies, there are no silver bullet here. 您可以使用许多策略,这里没有万灵药。 Just for example, you can decide to add parameter 'datasource's owner' to function above and controls it's lifetime this way. 仅举例来说,您可以决定将参数“数据源的所有者”添加到上面的函数中,并以此方式控制生存期。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM