【问题标题】:Convert delegate to Action<T> (Action<T1,T2>)将委托转换为 Action<T> (Action<T1,T2>)
【发布时间】:2012-09-09 19:24:15
【问题描述】:

我有一个 DB 类,可以进行如下所有 DB 调用:

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);
    }
}

在我的主课中,我是这样使用它的:

    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;
        });
    }

现在我的 DB 类中有 3 个方法,它们返回 3 组不同的数据,对于每种类型,我必须声明委托。
如果将来我要添加更多方法,那么我将不得不添加更多委托。

我可以删除委托的使用吗?我想简化我的类的构建,以便轻松添加新方法。

我需要的是能够像这样调用我的 DB 类:

DB.Instance.PartX(PartX_OK, ErrorHandler);

PartX 声明如下

private void PartX_OK(DataTable dt, int x, int y, ...)
{
//logic here
}

Action&lt;T&gt; 可以习惯这个,所以我可以用多个参数调用我的处理程序吗?如果是那怎么办?

【问题讨论】:

  • 您是说希望能够使用与Part2_Callback Part3_Callback 的值相同的方法处理程序吗?
  • 在主类中,我的 DB 类中的每个方法都有不同的处理程序。但是现在我必须为每个方法定义委托(如果它需要不同类型的参数或不同数量的参数),我想摆脱它。这样我就可以有一个通用委托或 2 个委托(一个用于一个参数,第二个用于两个参数)。正如我提到的,我想尝试使用Action&lt;T&gt;,但我不知道如何
  • 好的。在我看来,@JustinHarvey 有你想要的东西。

标签: c# generics .net-3.5


【解决方案1】:

是的,我的意思是回到你之前的问题,所以你的函数会变成

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);
    }

【讨论】:

  • 然后在我的主类中我这样调用我的方法:DB.Instance.Part2(Part2_OK, ErrorHandler) 所以我必须有两个CommandAndCallback 类?一个用于 1 个参数的操作,一个用于 2 个参数?
  • 不,CommandAndCallback 类是通用的,仅存储回调,因此不需要多个回调。
【解决方案2】:
  1. Part1_Callback 将等同于 Action&lt;string&gt;
  2. Part2_Callback 将等同于 Action&lt;DataTable&gt;
  3. Part3_Callback 将等同于 Action&lt;DataTable, int, int&gt;
  4. ErrorHandler 将等同于 Action&lt;string&gt;

只需使用这些类型来代替您现有的每个委托。

几乎不再有需要定义自己的委托的情况。如果您有 >16 个参数、ref/out 参数、params 参数或可选参数,那么您可能没有 Action/Func 重载,但这并不常见。

【讨论】:

  • 但是我应该如何定义我的CommandAndCallback 类?在 BeginExecuteReader 中作为参数传递?
  • @Misiu 怎么样?您几乎可以使用 Action 类型进行查找/替换来替换您的自定义类型。当您这样做时,您是否有任何问题或错误?
  • 所以不用改CommandAndCallback?我试图将其更改为奇怪的东西,但是当我将其保留为我的应用程序开始工作时:) 我将使用多个参数测试我的其余方法,并在遇到任何错误时回写。感谢您清除:)
  • 很遗憾我只能选择一个答案,因为每个答案都很有帮助,但由于代码示例,我选择了@Justin 的答案。
【解决方案3】:

Action 类具有多达 16 个通用参数,您可能会找到适合您需要的一个;)。见MSDN page

以及调用

DB.Instance.PartX((p1, p2, p3, p4) => { ... }, ErrorHandler);

【讨论】:

  • 很遗憾我只能选择一个答案,因为每个答案都很有帮助,但由于代码示例,我选择了@Justin 的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 2017-07-02
相关资源
最近更新 更多