Cel
Cel

Reputation: 6659

awaitable lambdas

Dynamic Evaluations in a Key Listener

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(){
        // ...
    }
}

This Lambda Should Await on Each Line

keyUpper.RegisterEvaluator(key => 
    {
    if (key == Key.A)
        if (key == Key.W)
            if (key == Key.A)
                return true;
    }
);

Upvotes: 2

Views: 2299

Answers (1)

svick
svick

Reputation: 244777

If asynchronicity is not a requirement and you are fine with having one thread that almost always waits, you could do it by giving the lambda some blocking way to access the key. For example:

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

…

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

RegisterEvaluator would then spin up a new thread that calls the lambda in a loop, passing it a method that accesses the key, blocking if no key is available at the moment, for example using BlockingCollection<Key>.

If you think doing it this way is wasteful (it is), and you can use async-await, just make the lambda async and change the passed in method to one that is asynchronous too:

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

…

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

Implementation of the latter version (using BlockBuffer<Key>) could look like this:

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
    }
}

Now, I'm not sure how exactly do you want to handle matching the keys, but I think it's not like this. For example, this sample code would match the sequence of keys BAW, but not AAW.

Another option would be not using Func<Task<Key>>, but your custom awaitable that can be awaited multiple times and gives one key each time you do that:

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

…

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

But I think doing it this way is more confusing and harder to do.

Upvotes: 2

Related Questions