8

is there a way to reference an object instance that is created using the "with" statement?

Example:

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

Where DoSomething would use the instance reference as if you were passing an instance from a variable declared reference to the object created.

Example:

AnObject := TAnObject.Create;

Thanks.

9 Answers9

16

Well, you can use such approach:

// 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;

or class helpers:

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;

But, actually, previous replies are correct: you should better forget about "with" statement.

Alex
  • 5,477
  • 2
  • 36
  • 56
  • 2
    +1 :) None of this helper stuff existed way back when I was originally doing Delphi. – cgp Apr 26 '09 at 18:25
  • 2
    funny to see this, as I wrote this blog post last week to be scheduled next week (when I will be really busy finishing up and preparing my leave for the Delphi Live! conference): http://wiert.wordpress.com/2009/04/27/delphi-bizarre-use-of-class-helpers-to-circumvent-with/ PS: pitty I can't read Russian and the google Russian -> English translator forgets a lot while translating your very interesting blog! – Jeroen Wiert Pluimers Apr 27 '09 at 13:57
  • Bevare that declating a helper for TObject would break any existing helper for TObject. There can only be one helper for each class. I don't like helpers... – Vegar Apr 28 '09 at 09:14
  • With statements are useful. They are not bad design practice. They serve well where they are applicable. – user30478 Jan 01 '19 at 23:29
12

You should never use with either because future changes might introduce more into that scope than you intended.

Take this for instance:

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

and then later on you tuck on a X property on the TSomeObject class. Now, which X do you think it's going to use? The local variable or the X property of the object?

The best solution is always to just create a local variable with a short name, and alias the object to that variable.

procedure Test;
var
    x: Integer;
    o: TSomeObject;
begin
    o := TSomeObject.Create;
    o.DoSomethingWithX(x);
    o.Free;
end;
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • 1
    Ok, i'll bite :-) 'with' can cause major confusion if you're not careful, but there are situations where it just makes code a lot more readable. Careful naming can be of some help here. Using naming schemes to avoid confusion is a common technique for Delphi code anyway (use TXxx for types, FXxxx for private members, GetXxx and SetXxx for getters and setters, etc). Just use MyXxx or something similar for local variables and you won't have as much confusion as you would have by calling variables "x" and "o". – Wouter van Nifterick Apr 27 '09 at 00:41
  • 1
    I would not trade the few cases where it *might* make it more readable for those occasions where I've been bit by the auto-scope feature of it. I have literally wasted weeks on problems related to this. No code can be justified as so readable that it's worth that much time in order to find odd bugs. – Lasse V. Karlsen Apr 27 '09 at 11:13
  • 1
    I agree on not using with. But I would advise to use a try after the create and a finally before the free. – Toon Krijthe Apr 27 '09 at 12:31
4

You gave the answer yourself: declare local variable. If you want you can use the with keyword on that.

var
  MyInstance: TMyObject;
begin
  MyInstance := TMyObject.Create;
  with MyInstance do
  try
    Foo;
    Bar;
    DoSomething(MyInstance);
  finally
    Free;
  end;
end;

In above example the only reason to use with is code readability, which is very subjective, you could also ditch the with keyword and use MyInstance directly. It's just a matter of personal taste. I don't agree on the "never use with" answers, but you should be aware of it's drawbacks.

See also this question: Is delphi "with" keyword a bad practice?

Community
  • 1
  • 1
The_Fox
  • 6,992
  • 2
  • 43
  • 69
2

An addition to Brian's example on a Notify handler is to use an absolute variable (win32 only):

procedure Notify( Sender : TObject ); 
var 
  Something : TSomeThing absolute Sender;
begin 
  if Sender is TSomething then 
  begin
    VerySimpleProperty := Something.Something;
    OtherProperty := Something.SomethingElse;
  end;
end;

It basically avoids having to assign a local variable or have a lot of type casts.

Gerry Coll
  • 5,867
  • 1
  • 27
  • 36
  • +1 for attending us on the use of 'absolute' (which long ago was introduced for absolute memory references, but your example is the only usage that is compatible with both the .net and the native world). – Jeroen Wiert Pluimers May 02 '09 at 06:45
  • I use this construct a lot, but have recently been made aware that it messes with the compiler's optimizer. Using this kind of construct can therefore lead to inefficient code generation, in case this is important (but can lead to easier readable code, IMO, in case speed isn't important). – HeartWare Sep 23 '14 at 14:25
1

I've learnt the hard way - only use 'With' in the following scenarios:

With TMyForm.Create( Owner ) do
  try
    ShowModal
  finally
    Free;
  end;


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

i.e keep the visibility of With as simple as possible. When you take into account the fact that the debugger cant resolve 'With', it's actually better and clearer to use a simple local variable or to fully declare the target i.e MyRecord.Something

Brian Frost
  • 13,334
  • 11
  • 80
  • 154
0

There is a working fine hack to do so. Define this workaround function somwhere in project unit.

// 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;

Usage example.

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

The best aproach in Delphi is use a variable to handle that instance.

Jota Brito
  • 11
  • 2
0

for FMX, you should use GetObject example:

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

This is not possible now, but we can make it a reality by persuading the compiler creators:

  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;

My proposal is:

  1. No more than one object/record per With header.
  2. Nested Withs not allowed.
  3. Usage of " to indicate the object/record (double quotes are similar to the ditto mark: http://en.wikipedia.org/wiki/Ditto_mark).