Передача действия для выполнения в отношении списка конкретных реализаций

#c# #lambda #interface #delegates #func

Вопрос:

Итак, у меня есть это

 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 {  Listlt;ITaskgt; list;  public TaskProxy (Listlt;ITaskgt; 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();  }  } }  

который я выполняю как

 class Program {  static Listlt;ITaskgt; list = new Listlt;ITaskgt;();   static void Main(string[] args)  {  list.Add(new Story());  list.Add(new Epic());   ITask task = TaskProxy(list );  task.PrintName("some props")  task.PrintState()  } }  

Но я хочу, чтобы вместо этого это было переписано как Actionlt;gt; generic, которое будет выполнять все аналогичные методы в контексте другой реализации. (что-то вроде заимствования метода?)

 public class TaskProxy : ITask {  Listlt;ITaskgt; list;  public TaskProxy (Listlt;ITaskgt; 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();  }  } }  

Комментарии:

1. Мне действительно непонятно, что ты пытаешься сделать. Чего бы вы ожидали от Funclt;gt; возвращения? Вы действительно ищете ан Actionlt;ITaskgt; ? (Функция возвращает что-то, действие-нет.) В качестве примечания я настоятельно рекомендую вам следовать обычным соглашениям об именах при написании примеров кода, подобных этому,-это позволяет избежать отвлекающих читателей.

2. Даже теперь, когда вы перешли к действию (в теле — вам все еще нужно написать название), неясно, что вы пытаетесь сделать. Вы можете принять Actionlt;ITaskgt; и позвонить action(tsk); по каждому пункту в списке… Хотя я не совсем понимаю, почему вы ожидаете, что сможете позвонить tsk.action()

3. Извините, у меня есть IInterfaceProxyImplementation, который выполняет все методы интерфейса по сравнению со всеми другими реализациями IInterface в цикле. попытка избежать цикла в каждой интерфейсной реализации IInterfaceProxyImplementation путем создания универсальных методов. Постараюсь быть более ясным в следующий раз

4. Нет, пожалуйста, на этот раз выразитесь яснее. Я сильно подозреваю, что если вы не отредактируете свой вопрос, чтобы прояснить его, вы не получите ответа. (Конечно, у меня недостаточно информации, чтобы ответить на этот вопрос.) Вы все еще хотите получить ответ на этот вопрос? Для начала было бы целесообразно определить интерфейс с несколькими методами, если речь идет о возможности выполнения нескольких методов…

5. @JonSkeet, надеюсь, на этот раз все будет более ясно

Ответ №1:

Общий вариант может выглядеть следующим образом:

 public void Generic(Actionlt;ITaskgt; iTaskMethodToExecute) {   foreach(ITask tsk in list)  {  iTaskMethodToExecute(tsk);  } }  

Пояснение: Actionlt;ITaskgt; означает действие, которое принимает ITask параметр as. Это позволяет получить доступ в лямбда-выражении ко всем методам, которые предоставляет ITask.

Вы бы назвали это тогда так:

 Listlt;ITaskgt; list = new Listlt;ITaskgt;();  list.Add(new Story()); list.Add(new Epic());  TaskProxy task = new TaskProxy(list ); task.Generic(x =gt; x.PrintName("some props")); task.Generic(x =gt; x.PrintState());  

На первый взгляд может показаться немного запутанным вставлять ITask параметр as в действие, как в этой строке:

 iTaskMethodToExecute(tsk);  

Но если вы посмотрите на вызов лямбда-выражения:

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

это должно иметь смысл, потому что только так компилятор сможет определить тип, и intelisense предложит даже методы автозаполнения:

введите описание изображения здесь

Комментарии:

1. Именно то, что мне нужно ! Большое вам спасибо! если у вас есть какие-либо хорошие учебные пособия по улучшению моих дженериков , я был бы вам признателен.

2. @Dexters Я рад, что смог помочь. к сожалению, учебные пособия по концентрации ничего не приходит на ум так быстро.

Ответ №2:

Для вашей цели размышление было бы жизнеспособным подходом. Решение может выглядеть следующим образом:

 using System; using System.Collections.Generic; using System.Reflection;  namespace CsharpPlayground {   public class Code  {  interface ITask  {  void printName();  }  class Story : ITask  {  public void printName()  {  Console.WriteLine("Story");  }  }  class Epic : ITask  {  public void printName()  {  Console.WriteLine("Epic");  }  }   public static void Main(string[] args)  {  var tasks = new Listlt;ITaskgt;  {  new Story(),  new Epic()  };   // Approach 1  Console.WriteLine("Approach 1");  foreach (var task in tasks)  {  task.printName();  }   // Approach 2  Console.WriteLine("nApproach 2");  void GenericExecuted(MethodInfo method)  {  foreach (ITask tsk in tasks)  {  method?.Invoke(tsk, new object[0]);  }  }  var printMethod = typeof(ITask).GetMethod(nameof(ITask.printName));  GenericExecuted(printMethod);  }  }  }  

Комментарии:

1. Спасибо, 2 — й подход-это то, что я пытался сделать, но пытался избежать размышлений.