Reference object instance created using “with” in

2019-01-12 04:26发布

问题:

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.

回答1:

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.



回答2:

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;


回答3:

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?



回答4:

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.



回答5:

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



回答6:

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).


回答7:

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;


回答8:

You should use Self.

Example:

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

In that context Self is the object created by with clause.