public override void Initialize(IApp app)
        {
            base.Initialize(app);

            Runners.ForEach(b =>
            {
                var iface = default(Type);

                if ((iface = b.Type.GetInterface(typeof(ICodeRunner <>).FullName)) == null)
                {
                    throw new ElideException("Runner '{0}' doesn't implement ICodeRunner<> interface.", b.Type);
                }

                var arg = iface.GetGenericArguments()[0];
                RunnerInstances.Add(arg, TypeCreator.New(b.Type));
            });
        }
        static async Task Main(string[] args)
        {
            var  runnersTasks  = new List <Task>();
            Task compositeTask = null;

            try
            {
                // run all the tasks and keep them in the list
                Runners.ForEach(r => runnersTasks.Add(r.RunAsync()));

                // just wait for them all to finish, and keep a reference to this compose task
                compositeTask = Task.WhenAll(runnersTasks);
                await compositeTask;
            }
            catch (Exception ex)
            {
                // note that the exception catched IS NOT the AggregateException, but the first exception thrown
                Console.WriteLine($"First exception catched: '{ex.Message}'");
                //usually just throw back the full AggregateException, rather than just the first exception catched here
                throw compositeTask.Exception;
            }
        }