将一个方法作为参数传递

8
我有一个名为SaveChanges<T>(T object)的方法,经常在我的代码中被调用。但是,根据调用该方法的操作不同,会从SaveChanges内部调用不同的方法。就像这样...
protected void SaveChanges<T>(T mlaObject, SomeFunction(arg))
    where T : WebObject
{
    try { this._db.SaveChanges(); }
    catch (Exception e)
    {
        Console.WriteLine("Error: " + e);
        SomeFunction(arg);
    }
}

使用示例:

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

我已经了解了委托方法,但是对于如何根据我的要求实现它或者我的要求是否需要使用委托还有些困惑。

编辑:另外,是否可以传递多个操作?

6个回答

10
如何呢:
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);
    }
}

被调用方式如下:

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

现在,我第二次阅读您的问题,发现传递mlaObject没有意义,因为它从未被使用。
// 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);
    }
}

你需要添加arg参数。 - George Duckett
我喜欢这个,非常直接。能否发送多个操作? - bflemi3
1
当然,你可以使用IEnumerable<Action<T>>或者你的Action<T>本身可以指定任意数量的要调用的内容,例如x => { doA(x); doB(x); doC(x); }。 - James Michael Hare
感谢 sixlettervariables。这非常准确无误。这是完美的。我真的很感激您抽出时间重新阅读。 - bflemi3
@bflemi3:当然,缩写为无参数。有点像delegate() { ... } - user7116
显示剩余4条评论

7

更新 我从您的问题中有点不清楚传递的参数是否在方法中的任何其他位置使用,看起来似乎没有,所以您可以只使用一个Action并使用lambda来指定要调用的委托和捕获的参数:

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

您可以传递的内容:

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

如果是针对myObj本身的情况(正如sixlettervariables已经回答的那样),您可以按照他的代码,在委托中直接将其传递回去。

或者,如果arg与mlaObject不同,并且您还想在代码中进行其他操作,那么可以这样做:

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

接下来有:

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

1
这段代码其实并不那么糟糕。它可能比你在简单的记事本应用程序中需要的更复杂,但真实世界的问题将使用语言的许多特性。这非常整洁和自我说明。最近你有看过一些 C 或 C++ 代码吗,Vash? - Kieren Johnstone
我对这段代码唯一的问题是 T mlaObject 的实用性,因为它未被使用。这可能不是针对 @JamesMichaelHare 的问题,但肯定是针对 OP 的 :) - user7116
我将重写SaveChanges,使其接受IEnumerable<Action<>>(正如James Michael Hare建议的那样),看看能否让它起作用。 - bflemi3
听起来是个不错的计划,如果有任何进一步的问题,请随时问。 - James Michael Hare
@bflemi3:另一种方法是使用params Action<T>[] actions,这将使代码看起来更加清晰。你也可以让该方法调用你的IEnumerable<Action<T>>重载方法。 - user7116
显示剩余4条评论

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

希望我正确理解了问题...


是的,你说得对。发送多个操作是否可能? - bflemi3

4

您的SaveChanges方法应该如下所示:

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

称为:

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

如果您提供了其中一个泛型参数,那么您必须提供两个参数的类型。 - user7116

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

使用以下方式调用:

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

我没有包含WebObject部分,因为它在函数中根本没有被使用。


0
如果我的需求需要使用委托,那么就使用它。
如果你想让SaveChanges方法执行某些功能,你有两个选择:
1. 让它直接执行该功能(在方法内部编写代码或从方法内部调用某些第二个方法); 2. 将该功能作为委托提供给SaveChanges方法。
何时使用这两种方法是一个设计选择,取决于场景、整体解决方案和你的偏好。
第一种方法的优点:
1. 能够在一个地方看到SaveChanges方法的每种可能结果; 2. 对不知道委托如何工作的人来说更少令人困惑。
第二种方法的优点:
  • 能够从SaveChanges方法中排除每个可能的函数(它不需要一个巨大的caseif else if else if
  • 传递给SaveChanges方法的函数可以在调用堆栈中位于其上方,它不需要知道它们是什么或如何工作,它们可以执行它不理解的操作,并且它们可以被重复使用-在其他函数中调用或用作委托。

我认为第一点是这里的主要问题。如果您只处理几种情况,那么使用if else if else if就可以了,但是如果您有多个选项并且更喜欢更通用的SaveChanges方法,则请使用psdd委托。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接