Why can't I assign my function reference to a matching variable? E2555 is raised

后端 未结 3 1276
情深已故
情深已故 2021-01-01 20:49

I\'m trying to build an custom comparer which allows the assignment of the comparison function to an internal field. In order to ease the creation of the comparer, I tried t

3条回答
  •  囚心锁ツ
    2021-01-01 21:08

    This is not a full fledged answer, rather notes to David's answer and to the topicstarter's question.

    Using answer mode for posting source snippets.

    class function TDemo.Construct: TDemo;
    begin
      Result := TDemo.Create();
      Result.FVar := Result.CompareInternal;
    end;
    
    class function TDemo.Construct: TDemo;
    var
      Demo: TDemo;
    begin
      Demo := TDemo.Create();
      Demo.FVar := Demo.CompareInternal;
      Result := Demo;
    end;
    

    Those both snippets use the same template:

    1. Create an object ( and memory management responsibilities attached to it )
    2. Tune and adjust the object
    3. Pass the object to outer world ( and m/m responsibilities with it )

    Sure, the p.2 here is just one single line, still

    1. It has a function call, which might be error prone. Twice so if function would be virtual overrode by inheriting subclasses.
    2. Patterns are to work not in the easiest situations, but rather in hardest ones.

    So I think we should assume that p.2 has risk of runtime error, risk of exception thrown. Then it is a textbook memory leak. The local function still holds the memory management responsibilities, since it did not passed the result outside. But it also does not fulfill the required cleanup.

    From my perspective the correct pattern - and the one giving one more incentive to using a dedicated local variable than mere Result/Result compiler confusion - should be

    class function TDemo.Construct: TDemo;
    var
      Demo: TDemo;
    begin
    
      Demo := TDemo.Create();  // stage 1: creating an object
      try                      // stage 1: accepting M/M responsibilities
    
         Demo.FVar := Demo.CompareInternal; // stage 2: tuning and facing
         // Demo.xxx := yyy;                //   ...potential risks of exceptions
         // Demo.Connect(zzz);  etc
    
         Result := Demo;   // stage 3: passing the object outside
         Demo := nil;      // stage 3: abandoning M/M responsibilities
         //  function exit should follow this line immediately, without other fault-risky statements
      finally
        Demo.Free;         // proceeding with M/M in case of faults in stage 2
      end;
    end;                   // stage 3: passing the object outside - immediately after the assignments!
    

    UPD: ventiseis: And as a side node: I would try to instantiate the configurated comparer TDemo only once. The comparison function should be a stateless function

      TDemo = class(TComparer)
      private
        class var FVar: TConstFunc;
       // function CompareInternal(const L, R: string): Integer; STATIC; // also possible
        class constructor InitComp;
      ...
      end;
    
      // would only be called once, if the class is actually used somewhere in the project
      class constructor TDemo.InitComp; 
      begin
        FVar := function(const L, R: string): Integer
        begin
          Result := StrToInt(R) - StrToInt(L)
        end 
      end;
    

提交回复
热议问题