6

I'm currently refactoring an existing DAL which has a facade the user calls and an inner class that does the actual work dependent upon the ADO.Net provider to use e.g. SqlProvider, and I'm trying to ensure that code is DRY, I've done ok by using a Func so I can do:

return RunCommand(c => c.ExecuteNonQuery(commandText, parameters));

And the RunCommand method looks like:

    private T RunCommand<T>(Func<Commands, T> toRun)
    {
        return toRun(CreateCommand());
    }

The CreateCommand() method simply builds the command object to use, this then allows me to have a single method that handles all the calls that just return the expected type e.g. DataSet, DataReader, etc

The problem I have is that several calls on the facade provide an out parameter I know should be able remove the repeated code if I can use a delegate but after a lot of googling & experimenting I've not managed to work out how. The code is:

 Commands commands = CreateCommand();
 return commands.ExecuteNonQuery(out cmd, commandText, parameters);

What I'd really like to do is be able to call:

return RunCommand(c => c.ExecuteNonQuery(out cmd, commandText, parameters));

I've seen this existing question but for the life of me I cannot work out how to turn that into what I need.

This delegate would seem to be what I need private delegate V TestOutParameter<T, U, V>(T a, out U b, V c); but the code I've got for calling it just isn't right:

    private V RunCommand<T, U, V>(TestOutParameter<Commands, DbCommand, V> commandToExecute)
    {
        DbCommand cmd;
        return (V)commandToExecute(CreateCommand(), out cmd);
    }

Can anybody help me as this has been driving me mad for a week!

Community
  • 1
  • 1
Nathan
  • 931
  • 1
  • 12
  • 26

1 Answers1

12

Your delegate has three parameters instead of two. Assuming you want it to mirror Func, instead of:

private delegate V TestOutParameter<T, U, V>(T a, out U b, V c);

you should have:

private delegate V TestOutParameter<T, U, V>(T a, out U b);

I would personally recommend you rename it to something like:

private delegate TResult FuncOut<T1, T2, TResult>(T1 arg1, out T2 arg2)
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • That delegate makes perfect sense, which is great. However I'm still scratching my head on how I go about calling it. The T1 is the class I need to call the actual method on and that has the out parameter on it. TBH I'm lost as to how to call it. – Nathan Jul 25 '11 at 09:55
  • @Nathan: The change in delegate type should make your `RunCommand` code just compile with no problem, so long as `CreateCommand` method returns a `Commands`. – Jon Skeet Jul 25 '11 at 10:00
  • See this is where I keep failing :) The `RunCommand` code is happy but when I attempt to use `return RunCommand(c => c.ExecuteNonQuery(out cmd, commandText, parameters));` I'm being told by VS that "cannot use ref or out parameters". I guess I'm not using the correct syntax for calling the method? – Nathan Jul 25 '11 at 10:12
  • 2
    @Nathan: Your lambda expression only has one parameter instead of two. It would need to be something like `(Commands c, out DbCommand cmd) => c.ExecuteNonQuery(out cmd, commandText, parameters))`. Unfortunately you can't use type inference in lambda expressions when your delegate has ref/out parameters. – Jon Skeet Jul 25 '11 at 10:21
  • Found the type inference issue when I put your revised call in, the 2nd parameter makes complete sense its definitely what I was missing. Due to the lack of inference does this effectively mean trying to do what I'm doing redundant as I need to be able to tell it the type? – Nathan Jul 25 '11 at 10:59
  • @Nathan: Well, you could potentially get type inference by calling a generic method which *returns* a delegate. Higher order programming is a bit of a mind melter, but it could work here... – Jon Skeet Jul 25 '11 at 11:01