简体   繁体   English

如何编写扩展方法,以使多播C#委托中的调用列表成员顺序运行?

[英]How to write extension method which will make invocation list members in multicast C# delegate run sequentially?

I have an async delegate which I await in the async method: 我有一个异步委托,我在异步方法中等待:

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();
}

The output is: 输出为:

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

That is, both invocation members go off simultaneously, not awaiting each other. 也就是说,两个调用成员同时启动,而不是彼此等待。 I need them await each other so the output would be: 我需要他们彼此等待,所以输出将是:

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

I tried this in place of await del() : 我尝试了这个代替await del()

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

This works. 这可行。 However, I have lots of code places where this needs to be done. 但是,我在很多代码位置都需要这样做。 Delegates can have different number of parameters of various types but they all return Task . 代表可以具有不同数量的各种类型的参数,但是它们都返回Task

How can I write an extension method which will let me run the code above by making calls like this? 如何编写扩展方法,使我可以通过这样的调用来运行上面的代码?

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[])'

If you use Func for you delegates rather than custom delegates you could write something like this: 如果您使用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

}

It does seem that you are trying to use delegates in ways they were not really intended. 看来您确实在尝试使用委托不是真正想要的方式。 They aren't really supposed to control order or have the functions rely on each other to complete. 他们并不是真正应该控制顺序或让函数相互依赖来完成。

Probably better off making a custom collection, maybe override += and -= if you want some delegate like behavior. 进行自定义集合可能更好些,如果您想要某种类似委托的行为,则可以覆盖+ =和-=。

The cause of your problem is that your delegates have a different set of parameters. 问题的原因是您的代表具有一组不同的参数。

The solution is to create an extra delegate that contains the call inclusive the parameters, similar to the System.Windows.Forms.MethodInvoker delegate . 解决方案是创建一个额外的委托,其中包含包含参数的调用,类似于System.Windows.Forms.MethodInvoker委托

The only difference in your methodInvoker is that your methodInvoker is not a delegate that returns void, but a delegate that returns a Task. methodInvoker的唯一区别是methodInvoker不是返回void的委托,而是返回Task的委托。

The function in your extension class would be similar to your foreach: 扩展类中的函数将类似于您的foreach:

public delegate task MethodInvoker();

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

Usage would be like: 用法如下:

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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM