1

I saw this code and I was wondering what the Action? does, after searching I saw this on MSDN "Encapsulates a method that has a single parameter and does not return a value." still unable to understand (application use cases) how to use it, is it a better replacement to ActionResult

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

 public class PrisonerDilemma 
 {
     public string PrisonerName { get; private set; }
     public StrategyBase Strategy { get; private set; }
     // What is this Action keyowrd doing?
     public Action? LastAction { get; private set; }        

     public Prisoner(string name, StrategyBase strategy)
     {
        if (strategy == null)
            throw new ArgumentNullException("strategy");

        PrisonerName = name;
        Strategy = strategy;
     }

     public void Do(Action? previousActionOfAnotherPrisoner)
     {
        if (previousActionOfAnotherPrisoner == null)
            LastAction = Strategy.Init();
        else
            LastAction = Strategy.Next(previousActionOfAnotherPrisoner.Value);
     }

 }

Edit 1: In ASP MVC

  1. What role does the Action? keyword play? and how is it used/leveraged.
  2. If Action does NOT take parameters and does NOT return a value, please help explain what is it good for, i.e. when is it typically used* in design patterns? passing/referring controller actions to child actions?
  3. If I wanted to use it as a Visitor or Strategy pattern, can it be passed across object boundaries like in C++? or is the scope restricted to the instance or Class or derived types?

Edit 2: Thanks for the explanation, its now clear that its function reuse. I found another post on SO that helps understand the differences between Action Vs Func, but not the typical use-case application, like whats a good use case to implement this. for e.g. in function reuse, can it be passed across object boundaries like in C++?

Community
  • 1
  • 1
Transformer
  • 6,963
  • 2
  • 26
  • 52

2 Answers2

5

Action and ActionResult have nothing to do with each other. Action has nothing to do with MVC. It's simply a generic delegate type (not a keyword), which is essentially a "template" that's used commonly enough by the framework that they decided to create the generic type so it could be reused frequently.

Action can take 0 on up to 16 parameters, all with a void return type. Func is the same, but has a return type.

In effect, you can use an Action anywhere you need to reference a function with a similar function signature.

So, for example, let's say you want to create a method that takes another method as a parameter. This "other method" could be typed as an Action (or Func) so that you don't have to hard code the type.

If you look at the first example from the Action documentation, there is an example of a normal delegate function. The second example uses an Action to do the same thing:

public static void Main()
{
  Action<string> messageTarget; 

  if (Environment.GetCommandLineArgs().Length > 1)
     messageTarget = ShowWindowsMessage;
  else
     messageTarget = Console.WriteLine;

  messageTarget("Hello, World!");   
}      

private static void ShowWindowsMessage(string message)
{
   MessageBox.Show(message);      
}

In this code, it references a function that takes 1 parameter, and a void return type. This can be mapped to an Action. Console.WriteLine() is also a method that takes 1 parameter and a void return type, so these are interchangeable if you treat them as Actions.

ActionResult, on the other hand is a type that encapsulates returned data from an MVC action (which is not a System.Action). For instance, if you return a ViewResult, it derives from ActionResult and encapsulates the data necessary to render a View. It has nothing to do with the generic delegate Action, just similarly named.

EDIT:

Based on comments below, you would implement your budget using an action like this:

public class MyBudget
{
    int _budgetId;
    int _deptId;
    public MyBudget(int budgetId, int deptId) 
    {
         _budgetId = budgetId;
         _deptId = deptId;
    }
    void CalcBudget()
    {
        // Do your budget code using _budgetId and _deptId
    }
}

public class GenericBudgetProcessor
{
    private Action _specificBudget

    public GenericBudgetProcessor(Action specificBudget)
    {
        _specificBudget = specificBudget;
    }

    public void DoTheBudget(Action specificBudget)
    {
        _specificBudget();
    }
}

Then you would have code somewhere else that does this:

var budget = new MyBudget(1, 1);
var processor = new GenericBudgetProcessor(budget.CalcBudget);
processor.DoTheBudget();

// Do a different budget
var budget2 = new YourBudget(2, 2);
processor = new GenericBudgetProcessor(budget2.CalcBudget);
processor.DoTheBudget();

This is just one example of how you might use an Action.

Or you could pass the parameters in the method

processor.DoTheBudget(1, 2);

In which case you would modify the above to use Action<int, int> instead of just Action.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • this makes sense, but when do you typically apply this, like whats a good use case to implement this. since you cannot pass actions across object boundaries or can you?? – Transformer Mar 19 '17 at 20:30
  • @transformer - I don't know what you mean by "object boundaries". If you mean, can you pass a method from one object to another, yes. Obviously you can. That's what a delegate is for. I think your problem here is that you don't understand what a delegate is. As for use cases, they're used all over the framework. For instance, the standard WinForms event handler is an `Action` – Erik Funkenbusch Mar 19 '17 at 20:46
  • I use delegates with IObservable, but if the `Action` is of a specific type, and I can assign it to any other class's `Action var`. Then I have crossed the boundary of my class, and it feels like I am passing the core implementation logic without the parameters and return type. Further its not a `static` so this is what has me hung up. This might work with turnSwitch On or OFF, but where it fails for me -- is I am unable to implement a [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern). For e.g. to implement a `CalculateBudget() : ICalculate` – Transformer Mar 19 '17 at 20:57
  • If I have `Dept class` and `Auditor class` different classes looking at budgets in various ways, I want them to have access to `PurchaseOrder Class` method and `Action? StrategySpecificBudget` =`CalculateBudget()` but I cant figure out, how their specific strategy is realized by their calls? – Transformer Mar 19 '17 at 21:00
  • @transformer - I really don't understand what you're getting at. Action in and of itself *IS* an implementation of the strategy pattern in and of itself. I don't understand your example though, a method can't inherit from an interface. Only classes (and structs obviously) can. – Erik Funkenbusch Mar 19 '17 at 21:04
  • Yes, the edit is exactly what I was asking about! now inside that, 1) can any class pass their specific `specificBudget`? What I am missing is, 2) when there are no parameters passed, for e.g. without an `int BudgetId, Dept Id` *how does that `specificBudget` know what budget to process?* how is the strategy conext passed between the Dept and the Strategy Implementer so that he can process the Budget – Transformer Mar 19 '17 at 21:26
  • @transformer - if you want to pass parameters, then pass parameters. I really don't understand what your problem is. Choose the right type for your needs. If you need to pass parameters, then use `Action`. If you don't, then your MyBudget or YourBudget classes would already know their specific budget id's, so when you instantiate them you would pass the id's in the constructor to those classes. The specific instance you create is the context. Notice I am passing an instance reference, not a class or static instance. – Erik Funkenbusch Mar 19 '17 at 21:31
  • @transformer - I modified the example to show parameters passed to the budget instances. – Erik Funkenbusch Mar 19 '17 at 21:35
  • @transformer - however, the `Action` version is not a true strategy implementation since typically, strategy instances know all the details of their implementation when they're instantiated, so the example that uses the constructor parameters is more "pure". – Erik Funkenbusch Mar 19 '17 at 21:51
3

Action is not a keyword, it is a type. The Action type is a delegate type, which means it represents a reference to a function. Action instances must reference functions that taken no parameters and return void. There are also generic versions of Action like Action<T1, T2>, which can be used to reference functions that take multiple parameters. Each generic parameter type represents the type of one parameter.

https://msdn.microsoft.com/en-us/library/system.action(v=vs.110).aspx

As far as the ? after the type name, that looks wrong to me. ? after a type makes that type nullable, but this can only be applied to value types. All delegate types are reference types, so any delegate can be assigned null as a value. It was my understanding that ? applied to a reference type was actually a compiler error.

Here is the documentation for the ActionResult type: https://msdn.microsoft.com/en-us/library/system.web.mvc.actionresult(v=vs.118).aspx

The use of the word "action" here is completely different than the usage in System.Action.

JamesFaix
  • 8,050
  • 9
  • 37
  • 73
  • your comment says "*they take no paremeters*" but then this signature `Action` looks like its taking type parameters - what am I missing? – Transformer Mar 19 '17 at 19:39
  • 1
    `Action` is a separate type from `Action`. There is a whole family of types from 0 to 8 parameters I believe. So there is `Action` representing something like `void MyMethod()`, `Action` representing `void MyMethod(T myParam)` ... `Action` representing `void MyMethod(T1 param1, T2 param2...T8 param8)` – JamesFaix Mar 19 '17 at 20:00
  • 1
    A very similar delegate family is `Func` which represents a function that does return something. The simplest `Func` is `Func` which represents a function that takes no parameters and returns something, like `int MyMethod()`, and they go up to 8 parameters too I believe, so `Func` represents `int MyMethod(T1 param1...T8 param8)` – JamesFaix Mar 19 '17 at 20:03
  • thanks. for the value type thats odd, because with the nullable`?` it compiles for me, strange! – Transformer Mar 19 '17 at 20:38
  • @transformer: That suggests you have more code that you haven't shown us. If you put the cursor on `Action` and hit F12, it will go to the declaration. – Jon Skeet Mar 19 '17 at 20:40