问题
I have a DB class that makes all DB calls like below:
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 CommandAndCallback<TCallback>
{
public SqlCommand Sql { get; set; }
public TCallback Callback { get; set; }
public ErrorHandler Error { get; set; }
}
class DB : SingletonBase<DB>
{
public static readonly string SqlConnectionString = @"Data Source=MyDB;Initial Catalog=Stats;Integrated Security=True;Asynchronous Processing=true;";
private DB()
{
}
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;
}
CommandAndCallback<Part2_Callback> ar = new CommandAndCallback<Part2_Callback>() { 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();
CommandAndCallback<Part2_Callback> ar = (CommandAndCallback<Part2_Callback>)result.AsyncState;
SqlDataReader dr;
if (result.IsCompleted)
{
dr = ar.Sql.EndExecuteReader(result);
}
else
dr = null;
dt.Load(dr);
dr.Close();
dt.Columns[3].ReadOnly = false;
ar.Callback(dt);
}
}
And in my main class I'm using it like so:
private void Form1_Enter(object sender, EventArgs e)
{
showStatus("Loading");
DB.Instance.Part2(Part2_OK, ErrorHandler);
}
private void ErrorHandler(string msg)
{
hideStatus();
viewStack1.InvokeIfRequired(c => { c.moveToFirst(); });
//MessageBox.Show(msg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private void Part2_OK(DataTable dt)
{
dataGridView1.InvokeIfRequired(c =>
{
c.DataSource = dt;
});
}
Right now I have 3 methods in my DB class that return 3 different sets of data, for every type I must declare delegate.
If in future I'll add more methods then I'll have to add more delegates.
Can I remove usage of delegates? I would like to simplify build of my class so that it will be easy to add new methods.
What I need is to be able to call my DB Class like so:
DB.Instance.PartX(PartX_OK, ErrorHandler);
PartX is declared as shown
private void PartX_OK(DataTable dt, int x, int y, ...)
{
//logic here
}
Can Action<T>
be used to to that, so I can call my handlers with multiple parameters? If yes then how?
回答1:
Yes, I meant to get back to your earlier question, so your function would become
public void Part2(Action<DataTable> 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;
}
CommandAndCallback<Action<DataTable>> ar = new CommandAndCallback<Action<DataTable>>() { 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();
CommandAndCallback<Action<DataTable>> ar = (CommandAndCallback<Action<DataTable>>)result.AsyncState;
SqlDataReader dr;
if (result.IsCompleted)
{
dr = ar.Sql.EndExecuteReader(result);
}
else
dr = null;
dt.Load(dr);
dr.Close();
dt.Columns[3].ReadOnly = false;
ar.Callback(dt);
}
回答2:
There are Action
classes with upto 16 generic parameters, you will probably find the one for your needs;). See MSDN page.
And the invocation
DB.Instance.PartX((p1, p2, p3, p4) => { ... }, ErrorHandler);
回答3:
Part1_Callback
will be equivilant toAction<string>
Part2_Callback
will be equivilant toAction<DataTable>
Part3_Callback
will be equivilant toAction<DataTable, int, int>
ErrorHandler
will be equivilant toAction<string>
Just use those types in place of each of your existing delegates.
There are almost no cases anymore where you need to define your own delegates. If you have >16 parameters, ref/out
parameters, params
parameters, or optional arguments then there may not be an Action/Func overload for you, but that's not particularly common.
来源:https://stackoverflow.com/questions/12461617/convert-delegate-to-actiont-actiont1-t2