简体   繁体   中英

Generic delegates with multiple parameters

I have a class that is responsible for all my Database actions, in general it calls stored procedures.

I've created 2 delegates in my class, one responsible for positive response (server returned OK for example) and second for all error handling.

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

I call all methods asynch like shown in my previous question: C# asynch SQL inside singleton and delegates

I have a problem when I need my delegate to return a different type of data.

For example my first method returns a String , second a DataTable , and third a DataTable and 2 ints .

Right now for every method I must create class that holds parameters:

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

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?

This way I'll be able to more easily control my code.

I found article on codeproject: http://www.codeproject.com/Articles/192027/Delegates-101-Part-III-Generic-Delegates but I don't know how to use this in my case :/

Should I declare my delegate like this:

delegate void MyDelegate (params object[] params);

or:

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

but this way I'll be able to pass only one parameter, I won't be able to use same delegate for 3 parameters.

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.

Can this be done?

EDIT:

I'll try to show my scenario:

In my main form class I'm calling my DB class like so:

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

My DB class looks like so:

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

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.

I would use the built in delegates I think,

Action<T>
Action<T, T>

etc

See here:

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

It looks like what you need is to create a generic CommandAndCallback class:

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

And, for example, where you previously used Part3_CommandAndCallback , you would now use CommandAndCallback<Part3_Callback> or even CommandAndCallback<Action<DataTable, int, int>> , if you didn't want to create a delegate type for each part either.

Also, public fields are generally discouraged, so you might consider changing them to autoproperties:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM