bflemi3
bflemi3

Reputation: 6790

Passing a method as an argument

I have a method SaveChanges<T>(T object) that is frequently called throughout my code, except depending on the action calling the method, there would be a different method called from within SaveChanges. Something like this...

protected void SaveChanges<T>(T mlaObject, SomeFunction(arg))
    where T : WebObject
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        SomeFunction(arg);
    }
}

Usage Examples:

SaveChanges<MlaArticle>(article, article.Authors.Remove(person)) //person is an object of type MlaPerson
//OR
SaveChanges<MlaArticle>(article, article.RelatedTags.Remove(tag)) //tag is an object of type Tag
//OR
SaveChanges<MlaArticle>(article, article.RelatedWebObjects.Remove(location)) //location is an object of type MlaLocation

I've read up on delegate methods but I'm a little confused as to how to implement this with my requirements or if my requirements warrant use for delegates at all.

EDIT: Also, would it be possible to pass multiple Actions?

Upvotes: 8

Views: 460

Answers (6)

Bjorn Coltof
Bjorn Coltof

Reputation: 509

protected void SaveChanges<T>(T mlaObject, Action<T> functionToCall)
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        functionToCall(mlaObject);
    } 
}

Call like this:

SaveChanges(actualArticle, article => article.Authors.Remove(person));

I left out the WebObject bit as it was not used in the function at all.

Upvotes: 0

Jamiec
Jamiec

Reputation: 136074

Your SaveChanges method would look something like:

protected void SaveChanges<T,TArg>(T mlaObject, TArg arg, Action<T,TArg> someFunction)
    where T : WebObject
{
   ...
}

Called like:

SaveChanges<MlaArticle,Person>(article,person, (article,person) =>  article.Authors.Remove(person))

Upvotes: 4

user7116
user7116

Reputation: 64068

How about:

protected void SaveChanges<T>(T mlaObject, Action<T> rollback)
    where T : WebObject
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        rollback(mlaObject);
    }
}

Called like:

this.SaveChanges(myObj, x => article.Authors.Remove(x));

Now, from a second read of your question, I don't see a point in passing the mlaObject as it is never used.

// this.SaveChanges(
//     () => article.Authors.Remove(author),
//     () => article.RelatedTags.Remove(tag));
protected void SaveChanges(params Action[] rollbacks)
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        foreach (var rollback in rollbacks) rollback();
    }
}

// Overload to support rollback with an argument
// this.SaveChanges(
//     author,
//     article.Authors.Remove,
//     authorCache.Remove);
protected void SaveChanges<T>(T arg, params Action<T>[] rollbacks)
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        foreach (var rollback in rollbacks) rollback(arg);
    }
}

Upvotes: 10

Kirk Broadhurst
Kirk Broadhurst

Reputation: 28698

if my requirements warrant use for delegates at all.

If you want the SaveChanges method to perform some function you have two options

  • have it perform the function directly (code inside the method, or call some second method from inside the method); or
  • provide the function to the SaveChanges method as a delegate.

When to use each of these is a design choice that is up to you and will depend on the scenario, the overall solution and your preference.

Benefits of the first

  • Able to see every possible outcome of the SaveChanges method in one place
  • Less confusing for someone who doesn't know how delegates work

Benefits of the second

  • Able to exclude every possible function from the SaveChanges method (it doesn't require an enormous case or if else if else if)
  • The functions that are passed to the SaveChanges method can be above it in the call stack, it doesn't need to know what they are or how they work, they can do things it doesn't understand, and they can be reused - called elsewhere or used as delegates in other functions.

I think the first point is the main one here. If you are only handling a couple of scenarios then it's okay to have a an if else if else if, but if you get more than a handful of options and prefer a more generic SaveChanges method then psdd that delegate.

Upvotes: 0

James Michael Hare
James Michael Hare

Reputation: 38397

UPDATE I was a little unclear from your question if the arg passed in is being used anywhere else in the method, it doesn't look like it is, so you can just take an Action and use a lambda to specify the delegate to call with the captured argument:

protected void SaveChanges<T, TArg>(T mlaObject, TArg arg, Action undoFunction)
    where T : WebObject
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        undoFunction();
    }
}

To which you can pass:

SaveChanges(article, () => article.Authors.Remove(person));

Or if it's the myObj itself, in which case (as sixlettervariables already answered) you can just pass it back in a delegate as per his code.

Or, whether the arg is different from mlaObject and you want to also do other things on it in the code, in which case you could do:

protected void SaveChanges<T, TArg>(T mlaObject, TArg arg, Action undoFunction)
    where T : WebObject
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        undoFunction(arg);
    }
}

And then have:

SaveChanges(article, person, article.Authors.Remove);

Upvotes: 7

BFree
BFree

Reputation: 103742

protected void SaveChanges<T,U>(T mlaObject, Action<U> action, U arg)
    where T : WebObject
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        action(arg);
    }
}

Hope I understood the question correctly...

Upvotes: 5

Related Questions