簡體   English   中英

如何編寫擴展方法,以使多播C#委托中的調用列表成員順序運行?

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

我有一個異步委托,我在異步方法中等待:

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

如果您使用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

}

看來您確實在嘗試使用委托不是真正想要的方式。 他們並不是真正應該控制順序或讓函數相互依賴來完成。

進行自定義集合可能更好些,如果您想要某種類似委托的行為,則可以覆蓋+ =和-=。

問題的原因是您的代表具有一組不同的參數。

解決方案是創建一個額外的委托,其中包含包含參數的調用,類似於System.Windows.Forms.MethodInvoker委托

methodInvoker的唯一區別是methodInvoker不是返回void的委托,而是返回Task的委托。

擴展類中的函數將類似於您的foreach:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM