Convert delegate to Action<T> (Action<T1,T2>)

穿精又带淫゛_ 提交于 2019-12-25 03:00:11

问题


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:


  1. Part1_Callback will be equivilant to Action<string>
  2. Part2_Callback will be equivilant to Action<DataTable>
  3. Part3_Callback will be equivilant to Action<DataTable, int, int>
  4. ErrorHandler will be equivilant to Action<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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!