【问题标题】:awaitable lambdas可等待的 lambda
【发布时间】:2012-01-22 17:15:42
【问题描述】:

关键监听器中的动态评估

public class KeyUpper {
    Func<Key, bool> _evaluate;

    public void RegisterEvaluator(Func<Key, bool> evaluate){
        _evaluate = evaluate;
    }

    public void KeyUp(object sender, KeyEventArgs e){
        if (_evaluate(e.KeyCode))
            SomeResponse();
    }

    public void SomeResponse(){
        // ...
    }
}

这个 Lambda 应该在每一行等待

keyUpper.RegisterEvaluator(key => 
    {
    if (key == Key.A)
        if (key == Key.W)
            if (key == Key.A)
                return true;
    }
);
  • 即客户端代码将对同一 key 参数提供一系列评估,并期望每一行评估都将被等待,以便在 1:A 2:W 3:A 的一系列 keyup 事件之后调用 SomeResponse()
  • 显然目前这永远不会发生,因为该方法一直运行到最后,而key == Key.W 永远不会为真
  • 这可能是不可能的,但是有没有办法让方法调用在它评估为 false 时自动从下一行返回,但在该行评估为 true 之前返回它,然后一直到之后的行,然后到方法结束之前的那一行?
  • 即是否有一种简单的方法来提供这种可等待的 lambda 表达式?

【问题讨论】:

  • 我不确定这里的问题是什么,这与 await 有什么关系。
  • 这个问题很难解释,严格来说它与awaitTasks 无关,尽管从概念上讲我要寻找的是等待功能
  • @cel - 你听说过响应式扩展吗?
  • @DanielA.White 我有点用 RX,但是您如何订阅订单要求 - 例如,您如何一般在 A 之后指定 W?

标签: c# .net delegates lambda async-await


【解决方案1】:

如果异步不是必需的,并且您可以拥有一个几乎总是等待的线程,您可以通过为 lambda 提供一些阻塞方式来访问密钥来做到这一点。例如:

public void RegisterEvaluator(Func<Func<Key>, bool> evaluate);

…

keyUpper.RegisterEvaluator(
    getKey => getKey() == Key.A && getKey() == Key.W);

RegisterEvaluator 然后会启动一个新线程,该线程在循环中调用 lambda,向它传递一个访问密钥的方法,如果此时没有可用的密钥,则阻塞,例如使用 BlockingCollection&lt;Key&gt;

如果您认为这样做很浪费(确实如此),并且您可以使用 async-await,只需将 lambda async 并将传入的方法更改为异步方法即可:

public Task RegisterEvaluator(Func<Func<Task<Key>>, Task<bool>> evaluate);

…

keyUpper.RegisterEvaluator(
    async getKey => await getKey() == Key.A && await getKey() == Key.W);

后一个版本的实现(使用BlockBuffer&lt;Key&gt;)可能如下所示:

class KeyUpper
{
    private readonly BufferBlock<Key> m_keyBuffer = new BufferBlock<Key>();

    public async Task RegisterEvaluator(
        Func<Func<Task<Key>>, Task<bool>> evaluate)
    {
        while (true)
        {
            if (await evaluate(m_keyBuffer.ReceiveAsync))
                SomeResponse();
        }
    }

    public void KeyUp(object sender, KeyEventArgs e)
    {
        m_keyBuffer.Post(e.Key);
    }

    private void SomeResponse()
    {
        // whatever
    }
}

现在,我不确定您究竟想如何处理匹配键,但我认为不是这样。例如,此示例代码将匹配键序列BAW,但不匹配AAW

另一种选择是不使用Func&lt;Task&lt;Key&gt;&gt;,但您的自定义可等待对象可以等待多次,并且每次执行时都会给出一个键:

public Task RegisterEvaluator(Func<KeyAwaitable, Task<bool>> evaluate);

…

keyUpper.RegisterEvaluator(
    async getKey => await getKey == Key.A && await getKey == Key.W);

但我认为这样做更令人困惑,更难做到。

【讨论】:

  • 感谢您的精彩回答!然而,最后我采用了 Rx 方法,因为我认为它可能是一种更强大的方式来提供流逻辑 - 所以注册签名变成了void Register(Func&lt;IObservable&lt;Key&gt;, IObservable&lt;Key&gt;&gt;);,我有可能使用像这样的运算符提供高级评估.Buffer(window open trigger, window close trigger etc)
猜你喜欢
  • 2019-12-12
  • 2021-12-11
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
  • 2014-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多