简体   繁体   中英

Passing methods as parameter vs calling methods directly

I have seen methods passed as parameters in some examples. If I can call one method from another method, why should I pass method as a parameter? What is the purpose behind this design?

  1. Calling one method from another
  2. Passing method as parameter using delegate or Action

Passing in a method as a parameter can be used to prevent dependencies and coupling. Let's take a look at how this can be used for the Strategy pattern:

Let's say we have a method PrintReport , which prints a given list of items, which might be sorted by Name or by Type, based on a parameter. This is the naive approach:

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

It's simple but it works. But what happens when we want to add a new sort order? We need to update the SortOrder enum, go into PrintReport and add a new case and call the new SortByWhatever method.

But if we passed in a method as a parameter, our PrintReport can be simpler and not care about the sort implementation:

public void PrintReport (List<Item> data, Func<List<Item>, List<Item>> sorter)
{
    List<Item> sortedItems = sorter(data);
    Print(sortedItems);
}

Now the sorting function can be defined anyway, possibly even in a different assembly that PrintReport isn't even aware of. It can be a lambda function or an anonymous method defined ad-hoc. But in all cases, our method will receive the delegate, use it to sort, and then print the report.

Here's a usage example. At first it looks like we merely moved the switch/case outside of the function, which is important enough since it allows different callers to have different logic. But watch for the third case.

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

Delegates are commonly used to decouple classes and interfaces from each other.

Here's a specific example. Suppose you had a UI class that was responsible for drawing a calendar, but you didn't want it to know exactly how to format the DateTime values into string.

You could define the class something like this:

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.
    }
}

That way, MyCalendarDrawer doesn't need to know how to format the dates - it is told how to do it by being passed a delegate Func<DateTime, string> that it can call to do so.

Treating functions as first class types has its advantages. It gives you functional programming possibilities.

Take the classic case of "Event Handling" for example, you will certainly send a function pointer to another function as a call-back on occurance of an event.

Similarly, here is another hypothetical example

private void CallMeBack(out int type, Func<int> action)
{
   type = action();
}

Now I can supply any function to this, like CallMeBack(a, ()=>return 1); and CallMeBack(a, ()=>return 2);

You should read about Delegates. As example, delegates are useful to define a dynamic callback on a given method completion.

Pseudo-code example:

doSomething(); //your code
updateInterface(continueDoingSomething); //a generic method, passing a delegate

...

doAnythingElse();
updateInterface(continueDoingAnythingElse);

In this example, you could define a generic method "updateInterface" which, as a callback, calls a dynamic method passed in as a delegate.

If not using delegates, you would have to implement two (or more) different methods:

void updateInterfaceAndContinueDoingSomething(){}
void updateInterfaceAndContinueDoingAnythingElse(){}

Truth is, every single example where functions are passed to other functions can be expressed in term of objects implementing a given interface passed to functions.

In other words, there are no obvious reasons delegates are better than interfaces. Upcoming lambdas in Java are an example than you don't really need to be able to pass a function to another function to be able to have a concise syntax.

In yet another words, the ability to pass a function to another function is just a tool in your programmer's toolkit just as passing objectd to functions is. And while this is arguable which is better, one can have a language that doesn't support passing functions to functions at all - Java - and still be able to have the same expressiveness.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM