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. Somethin开发者_如何学Gog 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?
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);
}
}
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);
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...
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))
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 enormouscase
orif 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.
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.
精彩评论