CommunityToolkit / CommunityToolkit/dotnet

[Feature] APIs to one-time subscribe to/await events

Aperta
#7 10 commenti 4 reazioni 0 assegnatari Vedi su GitHub
common 💼 feature request :mailbox_with_mail:
Lingua principale
C#
Stelle
3.8k
Fork
400
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

> Follow up from a request by @michael-hawker on his latest [Twitch stream](https://www.twitch.tv/videos/610404741?t=04h38m36s).

## Describe the problem this feature would solve

This issue is about having some APIs to allow users to subscribe to events just once, or to asynchronously await for an event to be raised, without having to manually define the handler, register/unregister the event from within the handler, and setup `Task`-s to await if needed.

_**Note:** that this issue is mostly just to gather feedbacks and discuss the idea at this point, I'm not actually opening a PR to add this feature. This is just to have a reference after the previous conversation._

## Describe the solution

I came up with a simple class that adds 3 methods that can be used to solve this issue. The signature can arguably seem a bit weird, but I don't think there's another way to do this at least at the moment, especially on UWP. Code generators might come in handy in the future, but those are still a ways off:

```csharp
public static class EventAwaiter
{
public static async Task Await(
Action register,
Action unregister,
THandler handler)
where THandler : Delegate;

public static async Task Await(
Action register,
Action unregister)
where THandler : Delegate;

public static async Task Await(
Action register,
Action unregister)
where THandler : Delegate
where TArgs : class;
}
```

The full code can be found in [my gist here](https://gist.github.com/Sergio0694/d91d59d230aca84dacd082ebb03ed63c).

The way these work is by creating a `TaskCompletionSource` behind the scenes, then using LINQ expression trees to wire up a custom handler that sets that task source, registering the handler, awaiting the task and then unregistering the handler. This is all completely transparent to the user, which just sees a nice API that supports the TPL workflow. Here's a sample usage:

```csharp
var button = new Button();

// BEFORE
TaskCompletionSource tcs = new TaskCompletionSource();

void Handler(object s, EventArgs e)
{
tcs.TrySetResult(null);
button.Loaded -= Handler;
}

button.Loaded += Handler;

await tcs.Task;

// AFTER
await EventAwaiter.Await(
h => button.Loaded += h,
h => button.Loaded -= h);
```

Those two lambdas to register/unregister events are needed right now as C# doesn't have proper support for registering events externally (this is by design, as far as I know).

## Describe alternatives you've considered

One possible alternative would be to leverage the APIs in the `System.Reactive` package, though that would require a whole new dependency just for this, and would still result in a very similar API surface for users anyway. Also that would probably involve even more overhead. With the proposed solution instead the whole thing is 100% self-contained and without external dependencies.

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Leggi prima il gist collegato e le firme EventAwaiter proposte; nell’issue non sono identificati file del repository né test. Prima dell’implementazione, stabilisci la forma supportata dell’API C#/UWP e i criteri di accettazione per la sottoscrizione una tantum e l’attesa degli eventi.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
csharp
Ambito
api
Tipo di issue
Funzionalità
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Ferma
Chiarezza
Da chiarire
Idoneità per principianti
25/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.