How it works
TaskCompletionSource separates the task observed by consumers from the code that completes it. A callback-based API, a local event, or a protocol response can supply that completion. Consumers await the Task property while the owner eventually chooses a result, an exception, or cancellation.
A task has one terminal transition. The first TrySetResult succeeds with seven, and the second returns false because completion already happened. Awaiting the task still produces seven. Try-style completion is useful when duplicate or competing signals are possible and the owner wants to handle the losing signal explicitly.
This example completes the task before awaiting it to keep the demonstration small and deterministic. A real event adapter may complete it later. RunContinuationsAsynchronously avoids directly running ordinary registered continuations inside the completion setter, which can reduce surprising reentrancy in the event-producing component.
Do not expose the TaskCompletionSource itself to every consumer. A caller that only needs to await the result should receive the task, while the component responsible for the operation keeps completion authority. Otherwise unrelated code could complete the operation with a fabricated result or cancel it before its true owner is finished.
Practice by using TrySetException as the first terminal signal and observing the failure through await. Then use TrySetCanceled with an already-canceled token. In each case, attempt a later TrySetResult and verify that the original terminal outcome remains unchanged.
Finally, sketch a callback adapter that unregisters its event handler when the operation completes. The task is only part of the lifecycle: subscriptions, cancellation registrations, and other owned resources still need cleanup. A reliable bridge defines who can complete it, how duplicate signals are treated, and who releases the surrounding event machinery.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
var completion = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
Console.WriteLine(completion.TrySetResult(7));
Console.WriteLine(completion.TrySetResult(9));
Console.WriteLine(await completion.Task);
Expected output
True False 7
TrySetResult reports whether this call won the single completion transition.