Delphi中使用“with”创建引用对象实例的方法

8

有没有一种方法可以引用使用“with”语句创建的对象实例?

例如:

with TAnObject.Create do
begin
  DoSomething(instance);
end;

在DoSomething函数中,您需要使用实例引用,就好像您从一个变量声明的引用传递实例一样。

例如:

AnObject := TAnObject.Create;

感谢您的选择。
9个回答

16

你可以采用以下方法:

// implement:

type
  TSimpleMethod = procedure of object;

function GetThis(const pr: TSimpleMethod): TObject;
begin
  Result := TMethod(pr).Data;
end;

// usage:

  with TStringList.Create do
  try
    CommaText := '1,2,3,4,5,6,7,8,9,0';
    ShowText(TStringList(GetThis(Free)));
  finally
    Free;
  end;
或者类助手:
type 
  TObjectHelper = class helper For TObject
  private
    function GetThis: TObject; Inline;
  public
    property This: TObject read GetThis;
  end;

...

function TObjectHelper.GetThis: TObject;
begin
  Result := Self;
end;

但是实际上,之前的回答是正确的:你最好忘记使用 "with" 语句。


2
当我最初使用Delphi时,没有这些辅助工具。+1 :) - cgp
2
很有趣看到这个,因为我上周写了这篇博客文章,计划在下周发布(当我忙于完成并准备参加Delphi Live!会议时): http://wiert.wordpress.com/2009/04/27/delphi-bizarre-use-of-class-helpers-to-circumvent-with/PS:可惜我不会阅读俄语,而且谷歌俄语->英语翻译器在翻译您非常有趣的博客时遗漏了很多内容! - Jeroen Wiert Pluimers
请注意,为TObject声明一个辅助程序将会破坏任何现有的TObject辅助程序。每个类只能有一个辅助程序。 我不喜欢辅助程序... - Vegar
With语句很有用。它们不是不良设计实践。它们在适用的情况下非常有用。 - user30478

12

你不应该使用with,因为未来的更改可能会将比预期更多的内容引入该作用域。

以这个为例:

procedure Test;
var
    x: Integer;
begin
    with TSomeObject.Create do
    begin
        DoSomethingWithX(x);
        Free;
    end;
end;

之后你在TSomeObject类上添加了一个X属性,那么你认为它会使用哪个X呢?本地变量还是对象的X属性?

最好的解决方案总是创建一个带有短名称的本地变量,并将对象别名为该变量。

procedure Test;
var
    x: Integer;
    o: TSomeObject;
begin
    o := TSomeObject.Create;
    o.DoSomethingWithX(x);
    o.Free;
end;

1
好的,我来解释一下 :-) 如果不小心使用“with”关键字,可能会导致很大的混乱,但在某些情况下,它确实可以使代码更易读。仔细命名可以在这里提供一些帮助。使用命名方案以避免混淆是Delphi代码的常见技术(例如,使用TXxx表示类型,FXxxx表示私有成员,GetXxx和SetXxx表示getter和setter等)。只需使用类似于MyXxx的本地变量,您就不会像调用变量“x”和“o”那样混淆了。 - Wouter van Nifterick
1
我不会为了让代码更易读而冒险使用自动作用域特性,因为这可能会导致一些问题。我曾经因此浪费了数周的时间来解决相关问题。没有任何代码可以被证明是如此易读,以至于值得花费那么多时间来查找奇怪的错误。 - Lasse V. Karlsen
1
我同意不使用with。但是我建议在创建后使用try,在释放之前使用finally。 - Toon Krijthe

4
你已经给出了答案:声明局部变量。如果你愿意,可以在其中使用with关键字。
var
  MyInstance: TMyObject;
begin
  MyInstance := TMyObject.Create;
  with MyInstance do
  try
    Foo;
    Bar;
    DoSomething(MyInstance);
  finally
    Free;
  end;
end;

在上面的例子中,使用with的唯一原因是代码可读性,这非常主观,您也可以放弃with关键字直接使用MyInstance。这只是个人口味问题。我不同意“永远不要使用with”的答案,但您应该意识到它的缺点。
另请参阅此问题:Delphi中的with关键字是否是一种不良实践?

2
在Brian的通知处理程序示例中,有一个额外的方法是使用绝对变量(仅适用于win32):
procedure Notify( Sender : TObject ); 
var 
  Something : TSomeThing absolute Sender;
begin 
  if Sender is TSomething then 
  begin
    VerySimpleProperty := Something.Something;
    OtherProperty := Something.SomethingElse;
  end;
end;

这基本上避免了分配本地变量或进行大量类型转换的需求。


感谢您参加我们关于“absolute”使用的讲解(这个词早在引入绝对内存引用时就已经存在,但是您的示例是唯一与.NET和本地环境兼容的用法)。 - Jeroen Wiert Pluimers
我经常使用这种结构,但最近意识到它会干扰编译器的优化器。因此,使用这种结构可能导致低效的代码生成,如果这很重要的话(但是在速度不重要的情况下,可以导致更易读的代码)。 - HeartWare

1
我曾经吃过亏 - 只有在以下情况下才使用“With”:
With TMyForm.Create( Owner ) do
  try
    ShowModal
  finally
    Free;
  end;


procedure Notify( Sender : TObject );
begin
  With Sender as TSomething do
    VerySimpleProperty := Something      
end;

保持 With 的可见性尽可能简单。考虑到调试器无法解析“With”这个关键字,使用一个简单的本地变量或者完全声明目标(即 MyRecord.Something)实际上更好、更清晰。

0

有一个可行的好方法来实现这个。 在项目单元中定义这个解决方法函数。

// use variable inside 'with ... do'
// WSelf function returns TObject associated with its method.
//   I would recommend to use the method 'Free'
// WSelf(Free) as <TObjectN>
type TObjectMethod = procedure of object;
function WSelf(const MethodPointer: TObjectMethod): TObject;
begin
  Result := TMethod(MethodPointer).Data;
end;

使用示例。

var
    SL: TStringList;
begin
    SL := TStringList.Create;
    try
        with TStringList.Create do
        try
            Add('1');
            Add('2');
            Add('3');
            // (WSelf(Free) as TStringList) references to the object
            //   created by TStringList.Create
            SL.Assign(WSelf(Free) as TStringList);
        finally
            Free;
        end;
    finally
        ShowMessage(SL.Text);
        SL.Free;
    end;
end;

0

在Delphi中最好的方法是使用一个变量来处理该实例。


0

对于 FMX,你应该使用 GetObject 函数 例如:

with TLabel.Create(box1) do
begin
    Font.Size := 34;
    Font.Style := [TFontStyle.fsBold];
    TextAlign := TTextAlign.taCenter;
    box1.AddObject(GetObject);
end;;

-1

现在还不可能,但我们可以通过说服编译器的创建者来实现它:

  With TForm1.Create (Nil) Do  // New TForm1 instance
    Try
      LogForm (");  // That same instance as parameter to an outer method (solution)
      "ShowModal;  // Instance.ShowModal
    Finally
      "Free;  // Instance.Free
    End;

我的建议是:

  1. 每个With头文件中不得超过一个对象/记录。
  2. 不允许嵌套的Withs。
  3. 使用“"”表示对象/记录(双引号类似于同上符号:http://en.wikipedia.org/wiki/Ditto_mark)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接