c# – SynchronizationContext.Send调用Func返回结果

我在当前项目中有一个简单的组件,可以从visual studio工具箱中删除到WinForms应用程序中的Forms和UserControls.

它工作正常 – 只是包装在ISupportInitialize.BeginInit(拥有表单/控件的UI上下文)期间捕获的SynchronizationContext的Send和Post方法.

以下是组件的最简单形式的现有代码.

public class SyncContextComponent : Component, ISupportInitialize
{
    private SynchronizationContext _context;

    public SyncContextComponent() { }
    public SyncContextComponent(IContainer container)
    {
        container.Add(this);
    }

    void ISupportInitialize.BeginInit()
    {
        _context = SynchronizationContext.Current; // context of creator
    }
    void ISupportInitialize.EndInit() { }

    /// <summary>
    /// Dispatches an <b>asynchronous</b> message to <see cref="Context"/>
    /// </summary>
    /// <param name="action">The delegate to call</param>
    /// <example>
    /// uiContext.Post(() => this.WorkOnUiControls());
    /// </example>
    public void Post(Action action)
    {
        _context.Post(new SendOrPostCallback(_ => action()), null);
    }

    /// <summary>
    /// Dispatches an <b>synchronous</b> message to <see cref="Context"/>
    /// </summary>
    /// <param name="action">The delegate to call</param>
    /// <example>
    /// uiContext.Send(() => this.WorkOnUiControls());
    /// </example>
    public void Send(Action action)
    {
        _context.Send(new SendOrPostCallback(_ => action()), null);
    }
}

我现在需要扩展它以支持返回值,并且正在考虑各种方式……

我有兴趣了解以下3种处理方法的含义,以及是否有“更好”的方法.

注意:我正在使用的示例是调用一个带有1个参数arg1的函数,并返回TResult,即Func< T1,TResult>.一旦我知道最好的方法,我打算扩展这个,所以我有来自Func< TResult>的重载.最多可能是9个Func< T1,T2 …. T9,TResult>以及Action重载.

Send0:第一种方式 – 是我已经在做的简单扩展:

    public TResult Send0<T1, TResult>(Func<T1, TResult> func, T1 arg1)
    {
        TResult retval = default(TResult);
        _context.Send(new SendOrPostCallback((x) =>
        {
            retval = func(arg1);
        })
        , null);
        return retval;
    }

发送1:第二种方式 – 是通过研究提示并查看有关stackoverflow的其他信息,这暗示我应该通过使用状态arg得到结果.我想如果是这样的话 – 我可能也需要对输入参数做同样的事情,所以我得到了以下内容:

    private class SendFuncState1<T1, TResult>
    {
        public TResult Result { get; set; }
        public T1 Arg1 { get; set; }
    }
    public TResult Send1<T1, TResult>(Func<T1, TResult> func, T1 arg1)
    {
        SendFuncState1<T1, TResult> state = new SendFuncState1<T1, TResult>()
        {
            Arg1 = arg1
        };
        _context.Send(new SendOrPostCallback((x) =>
        {
            var state0 = (SendFuncState1<T1, TResult>)x;
            state0.Result = func(state0.Arg1);
        })
        , state);
        return state.Result;
    }

Send2:第三次尝试 – 我认为自从我传递了args,也许我应该通过Func本身?无论如何 – 我得到以下内容:

    private class SendFuncState2<T1, TResult>
    {
        public Func<T1, TResult> Func { get; set; }
        public TResult Result { get; set; }
        public T1 Arg1 { get; set; }
    }
    public TResult Send2<T1, TResult>(Func<T1, TResult> func,
        T1 arg1)
    {
        SendFuncState2<T1, TResult> state = new SendFuncState2<T1, TResult>()
        {
            Func = func,
            Arg1 = arg1
        };
        _context.Send(new SendOrPostCallback((x) =>
        {
            var state0 = (SendFuncState2<T1, TResult>)x;
            state0.Result = state0.Func(state0.Arg1);
        })
        , state);
        return state.Result;
    }

所以,然后我把它放到一个小的测试应用程序中,并从后台线程更新WinForms控件测试每个方法,并在每个方法的10k次迭代上做一些秒表来测试各种方法的速度(仅使用字符串作为args和返回值)
它们都运行良好,在这个简单的测试中具有相同的性能.
不知道在使用非平凡的输入/输出类型时它会是什么样子.

所以 – Send0很好 – 剩下的就是无关紧要/乱了吗?
或者我应该考虑其他人 – 或其他什么?

最佳答案

They all worked fine

是的,他们都工作.使用最简单的 – 第一个.编译器将第一个转换为第二个(基本上). CLR级别不存在Lambda.为您创建一个隐藏的闭包类,并重写局部变量访问以访问该类的字段.

had the same performance

我敢肯定他们没有,但通过消息循环运行Windows消息是如此之慢,以至于它完全支持你正在做的简单操作.没有意义来优化这一点.

点赞