2017-01-20 4 views
0

У меня есть асинхронной делегат, который я жду в методе асинхронном:Как написать метод расширения, который заставит элементы списка вызовов в многоадресном делете C# запускаться последовательно?

async Task M1() 
{ 
    Debug.WriteLine("M1.A"); 
    await Task.Delay(10); 
    Debug.WriteLine("M1.B"); 
} 

async Task M2() 
{ 
    Debug.WriteLine("M2.A"); 
    await Task.Delay(1); 
    Debug.WriteLine("M2.B"); 
} 

delegate Task MyDel(); 

async void button_Click(object sender, RoutedEventArgs e) 
{ 
    MyDel del = null; 
    del += M1; 
    del += M2; 
    await del(); 
} 

Выход есть:

M1.A 
M2.A 
M2.B 
M1.B 

То есть, оба члены Призыва идут выключаться одновременно, не дожидаясь друг друга. Мне нужно их ждать друг друга, так что результат будет:

M1.A 
M1.B 
M2.A 
M2.B 

Я попытался это вместо await del():

foreach (MyDel member in del.GetInvocationList()) 
{ 
    await member(); 
} 

Это работает. Тем не менее, у меня много кодовых мест, где это нужно сделать. Делегаты могут иметь различное количество параметров различных типов, но все они возвращают Task.

Как написать метод расширения, который позволит мне запустить код выше, выполнив такие вызовы?

del0.AwaitOneByOne(); // del0 is 'delegate Task Method()' 
del1.AwaitOneByOne(paramInt1, paramStr2); // del1 is 'delegate Task Method(int, string)' 
del2.AwaitOneByOne(paramBytes1); // del2 is 'delegate Task Method(byte[])' 
+0

Это потоковая раса ошибка, вы не можете это исправить путем разбрызгивания волшебной феи пыли. Если в вашей программе много ошибок, вам придется переписать много кода. –

+0

Итак, в основном вам нужна коллекция задач, которые должны запускаться последовательно. Я прав? Может быть, проще просто написать класс именно для этого? По моему мнению, вы используете 'delegate', потому что он обеспечивает бесплатную добавление/удаление и подпись. Таким образом, как идея может быть проще взглянуть на «событие» и переопределить добавление/удаление в пользовательский метод, который будет связывать задачи вместо их параллелизма. Конечно, если вам нужно «поднять событие» за пределами своего владельца, будет непросто, поэтому вы можете абстрагироваться от этого в каком-то вспомогательном классе. – Lanorkin

+0

@ Lanorkin Возможно, для будущих проектов .. Решение от Mant101 будет достаточно в это время. – Alex

ответ

1

Если вы используете Func для вас делегатов, а не пользовательских делегатов вы могли бы написать что-то вроде этого:

public static class FuncHelper 
{ 
    public static async Task RunSequential<T1>(this Func<T1,Task> del, T1 param1) 
    {  
     foreach (var d in del.GetInvocationList().OfType<Func<T1, Task>>()) 
     { 
      await d(param1); 
     } 
    } 

    public static async Task RunSequential<T1, T2>(this Func<T1, T2, Task> del, T1 param1, T2 param2) 
    { 
     foreach (var d in del.GetInvocationList().OfType<Func<T1, T2, Task>>()) 
     { 
      await d(param1, param2); 
     } 
    } 

// Additional methods for more parameters go here 

} 

Это действительно кажется, что вы пытаетесь использовать делегат в том, как они реально не предназначены. На самом деле они не должны контролировать порядок или выполнять функции друг для друга.

Возможно, лучше создать пользовательскую коллекцию, возможно, переопределить + = и - =, если вы хотите, чтобы какое-то делегирование выполнялось как поведение.

+0

Спасибо.Я понимаю, что делегаты не могут быть идеальным решением, но это тонна старого кода, который уже сильно их использует, и мне просто нужно исправить некоторые ошибки. Рефакторинг с земли в настоящее время не является вариантом. – Alex

+0

Иногда вам просто нужно сделать все, что у вас есть. – Mant101

+0

@ Manl101 Упс. Теперь, на 'await del.RunSequential (1)' вызов я получаю '' MainPage.MyDel 'не содержит определения для' RunSequential 'и лучший метод перегрузки расширения' FuncHelper.RunSequential (Func , int) ' требуется приемник типа «Func ». Я изменил свое объявление делегата и методы для включения параметра int: 'делегировать задачу MyDel (int i)', 'async Task M1 (int i)', 'async Task M2 (int i)'. – Alex

-1

Причина вашей проблемы в том, что ваши делегаты имеют другой набор параметров.

Решение заключается в создании дополнительного делегата, который содержит вызов, включающий параметры, похожие на System.Windows.Forms.MethodInvoker delegate.

Единственная разница в методе methodInvoker заключается в том, что ваш методInvoker не является делегатом, который возвращает void, а делегатом, который возвращает задачу.

функция в классе расширения будет похожа на ваш Еогеасп:

public delegate task MethodInvoker(); 

static class DelegateExtensions 
{ 
    public static async Task ExecuteDelegates(this IEnumerable<MethodInvoker> methodInvokers) 
    { 
     foreach (var methodInvoker in methodInvokers) 
     { 
      await methodInvoker(); 
     } 
    } 
} 

Использования было бы как:

public MyClass 
{ 
    private async Task F1() 
    { 
     Debug.WriteLine("Begin F1"); 
     await Task.Delay(TimeSpan.FromSeconds(1)); 
     Debug.WriteLine("F1 Completed"); 
    } 

    private async Task F2(TimeSpan waitTime) 
    { 
     Debug.WriteLine("Begin F2"); 
     await Task.Delay(waitTime); 
     Debug.WriteLine("F2 Completed"); 
    } 

    private async Task F3(int count, TimeSpan waitTime) 
    { 
     Debug.WriteLine("Begin F3"); 
     for (int i = 0; i < count; ++i) 
     { 
      await Task.Delay(waitTime); 
     } 
     Debug.WriteLine("F3 Completed"); 
    } 
} 

public async Task ExecuteMyFunctions() 
{ 
    MyClass X = new MyClass(); 
    IEnumerable<MethodInvoker> myMethodInvokers = new MethodInvoker[] 
    { 
     () => X.F1(), 
     () => X.F2(TimeSpan.FromSeconds(1)), 
     () => X.F3(4, TimeSpan.FromSeconds(0.25)), 
    } 
    await myMethodInvokers.ExecuteDelegates(); 
} 
+0

Означает ли это, что мне нужно будет изменить все декларации этих делегатов? Для меня это похоже на то, что ваше решение обеспечивает совершенно другой способ делать что-то, а не обеспечивать «ожидание» способа вызова уже назначенных делегатов. – Alex

Смежные вопросы