35

I'm trying to create a list that contains methods, and after I add some methods I want to execute them, is this possible?

I tried something like this:

List<object> methods = new List<object>();

Then:

methods.Add(Move());

But When I add, the program will call the methods, for example, in this case it called for Move();

Ogglas
  • 62,132
  • 37
  • 328
  • 418
user3491915
  • 377
  • 1
  • 3
  • 6

5 Answers5

64

This is a great use case for the Action generic delegate.

List<Action> functions = new List<Action>();
functions.Add(Move);

foreach (Action func in functions)
   func();

If you need parameters, I would use lambdas to abstract them away:

List<Action> functions = new List<Action>();
functions.Add(Move);
functions.Add(() => MoveTo(1, 5));

foreach (Action func in functions)
   func();

A delegate is akin to function pointers from C++, it holds what a function "is" (not a return value like in your example) so you can call it just like a regular function. The Action generic delegate takes no parameters and returns nothing, so it is ideal for generic "call these functions".

MSDN for Action: Action Delegate

For more on the different types of delegates provided by.NET: https://stackoverflow.com/a/567223/1783619

BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • Is making a `List` necessary? Seems like simply defining `Action yourFunction;` and appending the methods to it works by `+=` them, like so `yourFunction += DoThis; yourFunction += DoThat;`. Only downfall is, I'm not quite sure how it iterates it internally, so if you need that level of control a List may be more appropriate. – chamberlainpi Jul 26 '17 at 17:51
  • @bigp You can do this because all delegates are multicast. I personally like being explicit here; if I want to use += I'll just use an event. As you say, its basically a style thing though – BradleyDotNET Jul 26 '17 at 18:42
  • The original question doesn't require parameters and returns nothing but the *title question* likely means people coming here may be looking for something else. It might be worth adding a link to 'Further Reading'. I would do it myself but am not familiar enough with the language. – AnnanFay Feb 27 '20 at 15:44
  • 1
    @AnnanFay I added a link to some extra info, hopefully that addresses your concern – BradleyDotNET Feb 27 '20 at 16:45
  • 1
    cannot convert from 'method group' to 'Action', i see this error when i try to do this – ReZ Jul 14 '20 at 03:51
  • then one of your methods isn't a no parameter/void method. @ReZ – BradleyDotNET Jul 14 '20 at 04:17
  • I wrote something like this: List> methods = new List>(); methods.Add(mymethod); – ReZ Jul 14 '20 at 04:23
  • public void mymethod(int x) { Console.WriteLine(x); }, it's give me a compilation error An object reference is required for the non-static field, method, or property – ReZ Jul 14 '20 at 04:25
  • @ReZ High probability the call to `Add` is in a static method (main perhaps?) which can't access instance members (like `mymethod`) without an instance. – BradleyDotNET Jul 14 '20 at 14:28
  • Yeah i could fix it, i'm called add in Main, that is a static function, thanks for your help – ReZ Jul 14 '20 at 18:16
10

I'm not sure if this outside the scope of the original question (or will help anyone else), but I kept coming back to this page in my own search of how to create a list of return-type functions to iterate and execute. I ended up using the List<Func<T>> to create a list of type methods-

        bool RTrue()
        {
            Console.WriteLine("RETURNS TRUE");
            return true;
        }

        bool RFalse()
        {
            Console.WriteLine("RETURNS FALSE");
            return false;
        }

        List<Func<Boolean>> functions = new List<Func<Boolean>>();
        functions.Add(RTrue);
        functions.Add(RFalse);

        foreach (Func<Boolean> func in functions)
        {
            if (func() == true)
            {
                Console.WriteLine("IT WORKS");
            }
        }

Further info on Func usage- What is Func, how and when is it used

2

You can use delegates. Create a list of delegates. Then, for each method you want to add to the list, create a delegate and add to the list.

 List<Delegate> methods = new List<Delegate>();

 // creates an Action delegate implicitly
 methods.Add((Action)Move);
Mike Dinescu
  • 54,171
  • 16
  • 118
  • 151
0

Depending on what parameters are required:

var Actions = new List<Action<object, EventArgs>>
{
    ReconfirmOrderlines_Click,
    ModifyOrderlines_Click
};

Or, for example, when you want to create a list to use for dynamiclly adding items in a ContextMenuStrip with the text and Click event:

var actions = new List<(string, Action<object, EventArgs>)>
{
    ("Send confirmation", ReconfirmOrderlines_Click),
    ("Send modification", ModifyOrderlines_Click),
};

If you want to know what you need to enter inside of the list, I always use this 'trick': Trick to get the parameters

Jannick Breunis
  • 195
  • 2
  • 14
0

Great solution from @BradleyDotNET and @HunterTheGatherer. However I had a third requirement and that was being able to tell which method had been called in case something failed.

This is my solution for uploads to Amazon S3 sending in parameters, reading the return value and showing what method failed if any method returns false.

var uploadFunctions = new List<Expression<Func<bool>>>();
uploadFunctions.Add(() => awsS3Service.UploadOrganisationJson(organisationJson, migrationId, orgId));
uploadFunctions.Add(() => awsS3Service.UploadUsersJson(usersJson, migrationId, orgId));
uploadFunctions.Add(() => awsS3Service.UploadContactPersonJson(contactPersonJson, migrationId, orgId));

foreach (var uploadFunction in uploadFunctions)
{
    var methodCall = uploadFunction.Body as MethodCallExpression;
    var methodName = "";
    if (methodCall != null)
    {
        //methodName will be "awsS3Service.UploadOrganisationJson" for the first item
        methodName = $"{methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}";
    }
    var func = uploadFunction.Compile();
    var success = func();
    if (!success)
    {
        SetErrorStatus(migration, customerMigration, $"Could not upload data for {methodName}");
    }
}

Source for Expression:

https://stackoverflow.com/a/20420544/3850405

Ogglas
  • 62,132
  • 37
  • 328
  • 418