【问题标题】:Generic delegates with multiple parameters具有多个参数的通用委托
【发布时间】:2012-09-17 09:33:51
【问题描述】:

我有一个类负责我的所有数据库操作,通常它调用存储过程。

我在班级中创建了 2 个委托,一个负责积极响应(例如服务器返回 OK),第二个负责所有错误处理。

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

我将所有方法都称为异步,如上一个问题所示:C# asynch SQL inside singleton and delegates

当我需要我的委托人返回不同类型的数据时,我遇到了问题。

例如,我的第一个方法返回一个String,第二个返回一个DataTable,第三个返回一个DataTable 和2 个ints

现在我必须为每个方法创建包含参数的类:

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

是否可以创建一个通用委托,以便我能够有一个用于响应的委托和一个用于参数的类?

这样我可以更轻松地控制我的代码。

我在 codeproject 上找到了文章:http://www.codeproject.com/Articles/192027/Delegates-101-Part-III-Generic-Delegates 但我不知道如何在我的情况下使用它:/

我应该这样声明我的代表吗:

delegate void MyDelegate (params object[] params);

或:

public delegate void MyDelegate2<T>(T param1);

但是这样我只能传递一个参数,我将无法对 3 个参数使用相同的委托。

哪种解决方案更好? 我想要一个通用委托,它能够接受一到三个不同类型的参数。

这个可以吗?

编辑:

我会尝试展示我的场景:

在我的主表单类中,我这样调用我的 DB 类:

    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 => { 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 =>
        {
            c.DataSource = dt;
        });
    }

我的 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 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<DB>
{

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

我的想法是同时以多种形式使用我的单例数据库控制器。因此,在第一种形式中,我会看到一些会自动刷新的统计信息,如果我愿意的话,第二种形式我将能够看到一些不同的统计信息,我可以在单击按钮时刷新这些统计信息。

【问题讨论】:

标签: c# generics delegates


【解决方案1】:

我认为我会使用内置的委托,

Action<T>
Action<T, T>

看这里:

http://msdn.microsoft.com/en-us/library/018hxwa8.aspx

【讨论】:

  • 我已经添加了我的班级的示例用法,你能告诉我如何在我的情况下使用Action吗?
【解决方案2】:

看起来您需要创建一个通用的CommandAndCallback 类:

public class CommandAndCallback<TCallback>
{
    public SqlCommand Sql;
    public TCallback Callback;
    public ErrorHandler Error;
}

而且,例如,您以前使用Part3_CommandAndCallback,如果您也不想为每个部分创建委托类型,现在可以使用CommandAndCallback&lt;Part3_Callback&gt; 甚至CommandAndCallback&lt;Action&lt;DataTable, int, int&gt;&gt;

此外,通常不鼓励公共字段,因此您可以考虑将它们更改为自动属性:

public class CommandAndCallback<TCallback>
{
    public SqlCommand Sql { get; set; }
    public TCallback Callback { get; set; }
    public ErrorHandler Error { get; set; }
}

【讨论】:

  • 这是一个想法 :) 但我能以某种方式对代表做同样的事情吗?我可以创建多个委托,这不是问题,但一个通用的会更好
  • 自动属性?嗯……我该怎么做?在这种特定情况下? :)
  • .Net 中已经有通用委托,例如Action&lt;T&gt;,用于具有一个参数的方法或Action&lt;T1, T2&gt;,用于具有两个参数的方法。你可以使用这些。
  • 我确实在我的答案的最后一个版本中提到了这一点:例如,您可以使用 CommandAndCallback&lt;Action&lt;DataTable, int, int&gt;&gt; 而不是 Part3_CommandAndCallback
  • 现在我正在使用你答案的第二部分CommandAndCallback&lt;TCallback&gt;,但是当我尝试使用Action&lt;T&gt; 时,我得到了错误。你能告诉我应该如何更改Part1Part1_Handler 以便Action&lt;T&gt; 可以工作吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
相关资源
最近更新 更多