title: Generic delegates with multiple parameters
body: <p>I have a class that is responsible for all my Database actions, in general it calls stored procedures.</p>
  
  <p>I've created 2 delegates in my class, one responsible for positive response (server returned OK for example) and second for all error handling.</p>
  
  <pre><code>public delegate void Part1_Callback(string message);
  public delegate void Part2_Callback(DataTable dt);
  public delegate void Part3_Callback(DataTable dt, int x, int y);
  public delegate void ErrorHandler(string message);
  </code></pre>
  
  <p>I call all methods asynch like shown in my previous question: <a href="https://stackoverflow.com/questions/12408835/c-sharp-asynch-sql-inside-singleton-and-delegates/12408957#12408957">C# asynch SQL inside singleton and delegates</a></p>
  
  <p>I have a problem when I need my delegate to return a different type of data.</p>
  
  <p>For example my first method returns a <code>String</code>, second a <code>DataTable</code>, and third a <code>DataTable</code> and 2 <code>ints</code>.</p>
  
  <p>Right now for every method I must create class that holds parameters:</p>
  
  <pre><code>public class Part1_CommandAndCallback
  {
      public SqlCommand Sql;
      public Part1_Callback Callback;
      public ErrorHandler Error;
  }
  
  public class Part2_CommandAndCallback
  {
      public SqlCommand Sql;
      public Part2_Callback Callback;
      public ErrorHandler Error;
  }
  
  public class Part3_CommandAndCallback
  {
      public SqlCommand Sql;
      public Part3_Callback Callback;
      public ErrorHandler Error;
  }
  </code></pre>
  
  <p>Is it possible to create one generic delegate so that I will be able to have one delegate for responses and one class for parameters?</p>
  
  <p>This way I'll be able to more easily control my code.</p>
  
  <p>I found article on codeproject: <a href="http://www.codeproject.com/Articles/192027/Delegates-101-Part-III-Generic-Delegates" rel="nofollow noreferrer">http://www.codeproject.com/Articles/192027/Delegates-101-Part-III-Generic-Delegates</a> but I don't know how to use this in my case :/</p>
  
  <p>Should I declare my delegate like this:</p>
  
  <pre><code>delegate void MyDelegate (params object[] params);
  </code></pre>
  
  <p>or:</p>
  
  <pre><code>public delegate void MyDelegate2&lt;T&gt;(T param1);
  </code></pre>
  
  <p>but this way I'll be able to pass only one parameter, I won't be able to use same delegate for 3 parameters.</p>
  
  <p>Which solution is better?
  I would like to have one generic delegate that will be able to take one-to-three parameters with different types.</p>
  
  <p>Can this be done?</p>
  
  <p><strong>EDIT:</strong></p>
  
  <p>I'll try to show my scenario:</p>
  
  <p>In my main form class I'm calling my DB class like so:</p>
  
  <pre><code>    private void form1_Enter(object sender, EventArgs e)
      {
          showStatus("Loading statistics...");
          DB.Instance.Part1(part1_ok,ErrorHandler);
          DB.Instance.Part2(part2_ok, ErrorHandler);
      }
      private void ErrorHandler(string msg)
      {
          hideStatus();
          //viewStack1.InvokeIfRequired(c =&gt; { c.moveToFirst(); });
          MessageBox.Show(msg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      }
  
      private void part1_ok(string msg)
      {
          MessageBox.Show(msg);
  
      }
  
      private void part2_ok(DataTable dt)
      {
          dataGridView1.InvokeIfRequired(c =&gt;
          {
              c.DataSource = dt;
          });
      }
  </code></pre>
  
  <p>My DB class looks like so:</p>
  
  <pre><code>public delegate void Part1_Callback(string message);
  public delegate void Part2_Callback(DataTable dt);
  public delegate void Part3_Callback(DataTable dt, int x, int y);
  public delegate void ErrorHandler(string message);
  
  public class Part1_CommandAndCallback
  {
      public SqlCommand Sql;
      public Part1_Callback Callback;
      public ErrorHandler Error;
  }
  
  public class Part2_CommandAndCallback
  {
      public SqlCommand Sql;
      public Part2_Callback Callback;
      public ErrorHandler Error;
  }
  
  public class Part3_CommandAndCallback
  {
      public SqlCommand Sql;
      public Part3_Callback Callback;
      public ErrorHandler Error;
  }
  
  class DB : SingletonBase&lt;DB&gt;
  {
  
      public static readonly string SqlConnectionString  = @"Data Source=MyDB;Initial Catalog=Stats;Integrated Security=True;Asynchronous Processing=true;";
  
      private DB()
      {
      }
  
      public void Part1(Part1_Callback callback, ErrorHandler error)
      {
          SqlConnection conn = new SqlConnection(SqlConnectionString);
          SqlCommand cmd = conn.CreateCommand();
          cmd.CommandType = CommandType.StoredProcedure;
          cmd.CommandText = "Part1";
  
          try
          {
              conn.Open();
          }
          catch (Exception ex)
          {
              error(ex.ToString());
              return;
          }
  
          Part1_CommandAndCallback ar = new Part1_CommandAndCallback() { Callback = callback, Error = error, Sql = cmd };
          IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(Part1_Handler), ar, CommandBehavior.CloseConnection);
      }
  
      private void Part1_Handler(IAsyncResult result)
      {
          string stats = string.Empty;
          Part1_CommandAndCallback ar = (Part1_CommandAndCallback)result.AsyncState;
          SqlDataReader dr;
  
          if (result.IsCompleted)
          {
              dr = ar.Sql.EndExecuteReader(result);
          }
          else
              dr = null;
  
          while (dr.Read())
          {
              stats += dr[0].ToString() + Environment.NewLine;
          }
          dr.NextResult();
  
          while (dr.Read())//problem is here
          {
              stats += dr[0].ToString() + " - " + dr[1].ToString() +Environment.NewLine;
          }
          dr.Close();
          ar.Callback(stats);
      }
  
      public void Part2(Part2_Callback callback, ErrorHandler error)
      {
          SqlConnection conn = new SqlConnection(SqlConnectionString);
          SqlCommand cmd = conn.CreateCommand();
          cmd.CommandType = CommandType.StoredProcedure;
          cmd.CommandText = "Part2";
  
          try
          {
              conn.Open();
          }
          catch (Exception ex)
          {
              error(ex.ToString());
              return;
          }
  
          Part2_CommandAndCallback ar = new Part2_CommandAndCallback() { Callback = callback, Error = error, Sql = cmd };
          IAsyncResult result = cmd.BeginExecuteReader(new AsyncCallback(Part2_Handler), ar, CommandBehavior.CloseConnection);
      }
  
      private void Part2_Handler(IAsyncResult result)
      {
          DataTable dt = new DataTable();
          Part2_CommandAndCallback ar = (Part2_CommandAndCallback)result.AsyncState;
          SqlDataReader dr;
  
          if (result.IsCompleted)
          {
              dr = ar.Sql.EndExecuteReader(result);
          }
          else
              dr = null;
  
          dt.Load(dr);
          dr.Close();
          ar.Callback(dt);
      }
  }
  </code></pre>
  
  <p>My idea was to use my singleton DB controller in many forms at the same time. So in first form I'll see some stats that will auto refresh, and if I'll like then is second form I'll be able to see some different stats that I'll be able to refresh on button click.</p>