我在一些例子中看到过作为参数传递的方法。如果我可以从另一个方法调用一个方法,为什么我应该将方法作为参数传递?这个设计背后的目的是什么?
Action
答案 0 :(得分:2)
将方法作为参数传递可用于防止依赖性和耦合。让我们来看看它如何用于策略模式:
假设我们有一个方法PrintReport
,它打印一个给定的项目列表,可以根据参数按名称或类型排序。这是天真的方法:
public void PrintReport (List<Item> data, SortOrder sortBy)
{
List<Item> sortedItems;
switch (sortBy)
{
case SortOrder.Name: sortedItems = SortByName(data); break;
case SortOrder.Type: sortedItems = SortByType(data); break;
}
Print(sortedItems);
}
这很简单但有效。但是当我们想要添加新的排序顺序时会发生什么?我们需要更新SortOrder枚举,转到PrintReport
并添加新的case
并调用新的SortByWhatever
方法。
但是如果我们将方法作为参数传递,我们的PrintReport
可以更简单,而不关心排序实现:
public void PrintReport (List<Item> data, Func<List<Item>, List<Item>> sorter)
{
List<Item> sortedItems = sorter(data);
Print(sortedItems);
}
现在无论如何都可以定义排序功能,甚至可能在PrintReport
甚至不知道的不同装配中。它可以是lambda函数或ad-hoc定义的匿名方法。但在所有情况下,我们的方法将接收委托,使用它进行排序,然后打印报告。
这是一个用法示例。起初看起来我们只是将开关/外壳移到了函数之外,这很重要,因为它允许不同的调用者具有不同的逻辑。但要注意第三种情况。
public void HandleData()
{
switch (ReportItemOrder)
{
case SortOrder.Name: PrintReport(data, SortByName); break;
case SortOrder.Type: PrintReport(data, SortByType); break;
case SortOrder.Whatever:
Func<List<Item>, List<Item>> customSort = (items) => /* do something */;
PrintReport(data, customSort);
}
}
答案 1 :(得分:1)
代理通常用于将类和接口相互分离。
这是一个具体的例子。假设您有一个负责绘制日历的UI类,但您并不希望它确切知道如何将DateTime值格式化为字符串。
您可以将类定义为:
public sealed class MyCalendarDrawer
{
private readonly Func<DateTime, string> _dateFormatter;
public MyCalendarDrawer(Func<DateTime, string> dateFormatter)
{
_dateFormatter = dateFormatter;
}
public void Draw()
{
// Do some work that involves displaying dates...
DateTime date = DateTime.Now;
string dateString = _dateFormatter(date);
// Display dateString somehow.
}
}
这样,MyCalendarDrawer
不需要知道如何格式化日期 - 它被告知如何通过传递它可以调用的委托Func<DateTime, string>
来完成它。
答案 2 :(得分:0)
将功能视为头等类型有其优点。它为您提供了函数式编程的可能性。
以“事件处理”的经典案例为例,您肯定会将一个函数指针发送到另一个函数,作为事件发生时的回调。
同样,这是另一个假设的例子
private void CallMeBack(out int type, Func<int> action)
{
type = action();
}
现在我可以为此提供任何功能,例如CallMeBack(a, ()=>return 1);
和CallMeBack(a, ()=>return 2);
答案 3 :(得分:0)
您应该阅读有关代表的信息。 例如,委托对于在给定方法完成时定义动态回调非常有用。
伪代码示例:
doSomething(); //your code
updateInterface(continueDoingSomething); //a generic method, passing a delegate
...
doAnythingElse();
updateInterface(continueDoingAnythingElse);
在此示例中,您可以定义一个通用方法“updateInterface”,它作为回调调用作为委托传入的动态方法。
如果不使用委托,则必须实现两种(或更多种)不同的方法:
void updateInterfaceAndContinueDoingSomething(){}
void updateInterfaceAndContinueDoingAnythingElse(){}
答案 4 :(得分:0)
事实是,函数传递给其他函数的每个例子都可以用实现传递给函数的给定接口的对象来表示。
换句话说,没有明显的理由代表比接口更好。 Java中即将推出的lambdas就是一个例子,而不是你真的需要能够将一个函数传递给另一个函数才能拥有简洁的语法。
换句话说,将函数传递给另一个函数的能力只是程序员工具箱中的一个工具,就像传递对象函数一样。虽然这可以说是更好的,但是人们可以拥有一种语言,它不支持将函数传递给函数 - Java - 并且仍然能够具有相同的表达能力。