title: How to use a helper generic of type class of type
body: <p>I am trying to figure out how to <em>genericize</em> this helper method; so that it returns the same type that was asked for:</p>
  
  <pre><code>type
      TScreenHelper = class helper for TScreen
      public
          function FindForm(DesiredFormClass: TFormClass): TForm;
      end;
  </code></pre>
  
  <p>Right now the caller has to cast the return value to type they already want:</p>
  
  <pre><code>var
     frmReportReminderSetup: TfrmReportReminderSetup;
  begin
     //frmReportReminderSetup := Screen.FindForm(TfrmReportReminderSetup); Doesn't compile
  
     frmReportReminderSetup := TfrmReportReminderSetup(Screen.FindForm(TfrmReportReminderSetup));
  </code></pre>
  
  <p>The non-generic implementation is:</p>
  
  <pre><code>function TScreenHelper.FindForm(DesiredFormClass: TFormClass): TForm;
  var
      f: TForm;
      i: Integer;
  begin
      Result := nil;
  
      for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
      begin
          f := Screen.Forms[i];
  
          if (f is DesiredFormClass) then
          begin
              Result := f;
              Exit;
          end;
      end;
  end;
  </code></pre>
  
  <h1>Generics</h1>
  
  <p>What i want is some way to use generics so that the function returns the type it was asked for.</p>
  
  <pre><code>frmContoso := Screen.FindForm(TfrmContoso);
  </code></pre>
  
  <p>In pseudo-code, the signature that i want would be something like:</p>
  
  <pre><code>function FindForm(T: TFormClass): T;
  </code></pre>
  
  <p>When it comes time to turn this into actual Delphi syntax, i <em>think</em> you must specify one of the  <strong>T</strong> references in angle brackets:</p>
  
  <pre><code>function FindForm(&lt;T&gt;): T;
  </code></pre>
  
  <p>But i don't think the <strong></strong> is allowed there; i think it has to be before the opening parenthesis:</p>
  
  <pre><code>function FindForm&lt;T&gt;: T;
  </code></pre>
  
  <h2>Try that</h2>
  
  <pre><code>TScreenHelper = class helper for TScreen
  public
      function FindFormOld(DesiredFormClass: TFormClass): TForm;
      function FindForm&lt;T&gt;: T;
  end;
  
  function TScreenHelper.FindForm&lt;T&gt;: T;
  var
      f: TForm;
      i: Integer;
  begin
      Result := nil;
  
      for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
      begin
          f := Screen.Forms[i];
  
          if (f is T) then
          begin
              Result := f as T;
              Exit;
          end;
      end;
  end;
  </code></pre>
  
  <p>Except that fails to compile:</p>
  
  <pre><code>Result := nil;  E2010 Incompatible types: 'T' and 'Pointer'
  </code></pre>
  
  <p>I can sorta see what's wrong. It doesn't understand that <strong>T</strong> is a class (i.e. what if it was an <code>Integer</code>? Then it would absolutely be wrong to set it to <code>nil</code>.)</p>
  
  <h1>Constraints</h1>
  
  <p>So i need to somehow give the compiler the hint of what type <code>T</code> <strong>will</strong> be:</p>
  
  <pre><code>TScreenHelper = class helper for TScreen
  public
      function FindForm&lt;T: TFormClass&gt;: T;
  end;
  
  function TScreenHelper.FindForm&lt;T: TFormClass&gt;: T;
  var
      f: TForm;
      i: Integer;
  begin
      Result := nil;
  
      for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
      begin
          f := Screen.Forms[i];
  
          if (f is T) then
          begin
              Result := f as T;
              Exit;
          end;
      end;
  end;
  </code></pre>
  
  <p>This new signature is confusing to call; you no longer <strong>pass</strong> a desired type to the function. Instead you now <strong>call</strong> a variation of the function that you want:</p>
  
  <pre><code>frmContoso := Screen.FindForm&lt;TfrmConsoto&gt;();
  </code></pre>
  
  <p>But no matter; it's way of generics. </p>
  
  <h1>Except that's not valid</h1>
  
  <p>The syntax:</p>
  
  <pre><code>function FindForm&lt;T: TFormClass&gt;: T;
  </code></pre>
  
  <p>is not valid, as <code>TFormClass</code> is not one of the allowed Delphi constraint types:</p>
  
  <blockquote>
    <h2><a href="http://docwiki.embarcadero.com/RADStudio/XE6/en/Constraints_in_Generics" rel="nofollow">Constraints in Generics</a></h2>
    
    <p><strong>Specifying Generics with Constraints</strong></p>
    
    <p>Constraint items include:</p>
    
    <ul>
    <li>Zero, one, or multiple interface types</li>
    <li><strong>Zero or one class type</strong></li>
    <li>The reserved word "constructor", "class", or "record"</li>
    </ul>
  </blockquote>
  
  <p><em>(emphasis mine)</em></p>
  
  <p>While i'm allowed <em>one class type</em>, i'm not passing a class type; i'm passing a <strong>class of class</strong> type.</p>
  
  <p>So now i'm stuck. In my attempt to save myself typing 25 characters, i've now blown an hour on the minutia of Delphi generics.</p>
  
  <h1>tl;dr</h1>
  
  <p>How do i <em>genericize</em>:</p>
  
  <pre><code>function FindForm(DesiredFormClass: TFormClass): TForm;
  </code></pre>
  
  <h2>Example non-working code</h2>
  
  <pre><code>program Project2;
  
  {$APPTYPE CONSOLE}
  
  {$R *.res}
  
  uses
    System.SysUtils, Vcl.Forms;
  
  type
      TScreenHelperCore = class(TObject)
      public
          class function FindForm&lt;T: TForm&gt;: T;
      end;
  
      TfrmContoso = class(TForm)
      public
  
     end;
  
  { TScreenHelperCore }
  
  class function TScreenHelperCore.FindForm&lt;T: TForm&gt;: T; 
  //                                         \__[dcc32 Error] Project2.dpr(23): E2029 ',', ';' or '&gt;' expected but ':' found
  var
      f: TForm;
      i: Integer;
  begin
      Result := nil;
  
      for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
      begin
          f := Screen.Forms[i];
  
          if (f is T) then
          begin
              Result := f;
              Exit;
          end;
      end;
  end;
  
  var
      f: TfrmContoso;
  
  begin
    try
          f := TScreenHelperCore.FindForm&lt;TfrmContoso&gt;;
          if f = nil then
              f := TfrmContoso.Create(nil);
  
          f.ShowModal;
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  end.
  </code></pre>