Is there any fundamental difference between to using an action and a list of actions? Seeing that action is a delegate and therefore is a list itself. For instance:

List<Action> technicallyRedundant = new List<Action>();
technicallyRedundant.Add(() => { Console.WriteLine("Action 1"); });
technicallyRedundant.Add(() => { Console.WriteLine("Action 2"); });

Is this architecture more readable? Which would be recommended for use when exposing for an external library?

Action act = null;
act += () => { Console.WriteLine("Action 1"); }
act += () => { Console.WriteLine("Action 2"); }
  • What’s the context? A list is more explicit and can help with readability. And multicast delegates have specific behaviors (especially around exception handling) which are occasionally not wanted.
    – Telastyn
    Mar 12, 2018 at 1:06
  • 1
    What do you mean by "proper?" You can't enumerate the subscribers of an event from outside of the class contained in the event. Doesn't that pretty much disqualify its use as a List? Mar 12, 2018 at 1:21
  • @RobertHarvey actions have a function GetInvocationList so you can enumerate them. Also this is an action not an event
    – johnny 5
    Mar 12, 2018 at 1:52
  • 2
    @johnny5: You're responding to a comment that I didn't make. Read the comment again. In addition, an event is just a delegate with a specific method signature, so you're making a distinction that doesn't matter. Mar 12, 2018 at 2:11
  • 1
    @johnny5: No, I'm saying that there's no material difference. Look at the code in the OP's example above. Anywhere you see a +=, you're looking at a delegate. You even said it yourself: there's a GetInvocationList() method. Mar 12, 2018 at 2:55

2 Answers 2


Yes, there is a fundamental difference between the two: type safety.

Whilst GetInvocationList can indeed be called on act in your example, it doesn't return a list, it returns an array of Delegate. As a result, the slower DynamicInvoke must be used to run it and the wrong parameters can easily be passed to it, as demonstrated by the following compilable code:

Action act = null;
act += () => { Console.WriteLine("Action 1"); };
act += () => { Console.WriteLine("Action 2"); };
foreach (var action in act.GetInvocationList())

Run this and it'll throw a TargetParameterCountException.

Whereas with List<Action>, we have a strongly typed list. In any iteration of it, we'd be dealing with Action, rather than Delegate. Thus Invoke() can be used, which knows the parameter requirements.

  • can’t you cast the delegate back to action and call a regular invoke?
    – johnny 5
    Mar 12, 2018 at 18:13
  • @johnny5: I don't see how that would help you with anonymous delegates. You'd have to cast it to an Action<T> for some concrete type. Mar 12, 2018 at 20:41
  • @johnny5: Have a look here: faithlife.codes/blog/2008/07/casting_delegates Mar 12, 2018 at 20:49
  • @RobertHarvey interesting but doesn’t that mean in order to subscribe to the action in the first place you would have to be of type action? So wouldn’t it be safe to assume that all multicast delegates are actually the same type in the invocation list as the declaring, as outlined in this answer
    – johnny 5
    Mar 12, 2018 at 21:03
  • 2
    @johnny5, sure you can cast it back. But that requires you know the type (obvious here, less so in real-world scenarios) and really, why why assign to an array of Delegate and then have to cast back again, when List<Action> can be used to maintain the correct type directly?
    – David Arno
    Mar 13, 2018 at 8:06

The main difference is internal. Delegate is a linked list; List uses an array internally. For a public API, you should not use either. List is not supposed to be exposed in properties, and a public delegate is usually an event. Perhaps you want a delegate parameter instead.

As the other poster said, List is strongly typed, but an event can also be strongly typed. The delegate would then be hidden in generated code.

Check out the .net Framework Design Guidelines for public collection property and event/delegate guidelines.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.