Si vous êtes abonnes a ce flux, je suppose que vous connaissez déjà C# async (avec les mots-clef async et await). Ce que vous ne savez par contre peut-etre pas, c’est qu’il est tout a fait possible de creer vos propres trucs awaitable.

 

En effet, comme cela était déjà le cas avec la syntaxe Linq a l’époque de C# 3 (mots clefs where, select etc.), le mot clef “await” ne fait que déclencher un traitement de precompilation. En gros, await attend un truc, sans trop savoir ce qu’il attend. Si j’ecris la syntaxe :

 

Console.WriteLine("Calcul asynchrone d'un truc vachement complique...");
int value = await DoSomethingVeryComplicated();
Console.WriteLine(value);

 

Apres l’etape de precompilation, le compilateur voit quelque chose du genre :

 

TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Console.WriteLine("Calcul asynchrone d'un truc vachement complique...");
var awaiter = DoSomethingVeryComplicated().GetAwaiter();
awaiter.OnCompleted(() =>
   {
      int value = awaiter.GetResult();
      Console.WriteLine(value);
      tcs.SetResult(null);
   });
return tcs.Task;

 

(je dis bien du genre, parce que je suis pas le compilateur donc je ne vois pas vraiment ce qu’il voit)

 

Un des elements qui semble completement anodin, mais qui fait la toute surpuissance du machin, c’est le “var” a la 3e ligne. En effet, un peu comme le mot clef Where cherche une methode ayant la signature Where(Func<T,bool> predicate) ce qui permet de lui faire faire un peu tout et n’importe quoi, await cherche une methode GetAwaiter() qui renvoit quelque chose ayant une methode OnCompleted(Action completion) et une methode GetResult(). Et tant que ces methodes sont presentes, ca marche !

 

Par exemple, je peux ecrire cette classe :

 

    public class MaFakeTaskJustePourEmbeterLeCompilo
    {
        private int _result;
        private bool _completed;
        private List<Action> _completions = new List<Action>();
 
        public MaFakeTaskJustePourEmbeterLeCompilo()
        {
            // truc asynchrone parce que je le veux bien
            Task.Factory.StartNew(() =>
                                      {
                                          Thread.Sleep(3000);
                                          _result = 42;
                                          NotifyCompleted();
                                      });
        }
 
 
        private void NotifyCompleted()
        {
            _completed = true;
            lock (_completions)
            {
                foreach (var c in _completions)
                    c();
                _completions.Clear();
            }
        }
 
        public void OnCompleted(Action c)
        {
            // ce code ne gere pas le SynchronisationContext courrant pour plus de lisibilite.
            // ne pas utiliser tel quel dans une appli WP7 !
            lock (_completions)
            {
                if (_completed)
                {
                    c();
                }
                else
                    _completions.Add(c);
            }
        }
        public int GetResult()
        {
            return _result;
        }
 
        public bool IsCompleted
        {
            get { return _completed; }
        }
 
        public MaFakeTaskJustePourEmbeterLeCompilo GetAwaiter()
        {
            return this;
        }
    }

 

et l’utiliser ensuite avec async :

 

Console.WriteLine("Calcul asynchrone d'un truc vachement complique...");
var value = await new MaFakeTaskJustePourEmbeterLeCompilo();
Console.WriteLine(value);

 

Maintenant, vous savez comment faire attendre un truc rigolo au mot clef await. Youhou!

 

(plus serieusement, ca peut permettre d’utiliser la syntax-sugarite de async avec des trucs pas prevus pour, et ca, c’est cool)