Search code examples
c#lambdainterfacedelegatesfunc

Passing an Action<interface> to to be executed against list of concrete implementations


So I have this

public interface ITask
{
    void PrintName(string props);
    void PrintState();
}

public class Epic: ITask
{
    public Epic() { }
    public void PrintName(string props)
    {
        Console.WriteLine("Hello Epic: " + props);
    }
    public void PrintState(string props)
    {
        Console.WriteLine("Epic Started");
    }
}


public class Story: ITask
{
    public Story() { }
    public void PrintName(string props)
    {
        Console.WriteLine("Hello Story: " + props);
    }
    public void PrintState(string props)
    {
        Console.WriteLine("Story Started");
    }
}

public class TaskProxy : ITask
{
    List<ITask> list;
    public TaskProxy (List<ITask> list) 
    { 
        this.list = list;
    }
    public void PrintName(string props)
    {            
      foreach(ITask tsk in list)
      {
        tsk.PrintName(props);
      }
    }
    public void PrintState()
    {            
      foreach(ITask tsk in list)
      {
        tsk.PrintState();
      }
    }
}

which I am executing as

class Program
{
    static List<ITask> list = new List<ITask>();

    static void Main(string[] args)
    {
        list.Add(new Story());
        list.Add(new Epic());

        ITask task = TaskProxy(list );
        task.PrintName("some props")
        task.PrintState()
    }
}

But I want this instead to be re-written as Action<> generic which will execute all similar methods in the context of a different Implementation. (something like method borrowing?)

public class TaskProxy : ITask
{
    List<ITask> list;
    public TaskProxy (List<ITask> list) 
    { 
        this.list = list;
    }

    public void PrintName(string props)
    {            
       Generic(ITask.PrintName(props)) // looking for something like this    
    }

    public void PrintState()
    {            
       Generic(ITask.PrintState())  // looking for something like this   
    }

    public void Generic(Action methodToExecute)
    {            
       foreach(ITask tsk in list)
       {
          tsk.methodToExecute();
       }
    }
}

Solution

  • A generic variant could look like this:

    public void Generic(Action<ITask> iTaskMethodToExecute)
    {            
        foreach(ITask tsk in list)
        {
            iTaskMethodToExecute(tsk);
        }
    }
    

    Explanation: Action<ITask> stands for an action that takes an ITask as parameter. This allows you to access in a lambda expression all the methods that ITask provides.

    You would call it then like this:

    List<ITask> list = new List<ITask>();
    
    list.Add(new Story());
    list.Add(new Epic());
    
    TaskProxy task = new TaskProxy(list );
    task.Generic(x => x.PrintName("some props"));
    task.Generic(x => x.PrintState());
    

    On the first glance it might look a little confusing to insert a ITask as parameter into the action as in this line:

    iTaskMethodToExecute(tsk);
    

    But if you look at the lambda expression call:

    task.Generic(x => x.PrintState());
                 ^
                 |
             input of type: ITask
    

    it should make sense because only this way the compiler is able to infer the type and intelisense will suggest even the methods in autocomplete:

    enter image description here