Lorsque vous écrivez une fonction d'itérateur en C#, en utilisant la syntaxe yield, le compilateur traduit en interne votre fonction dans une classe. Donc, si je vous écris quelque chose comme ceci:Recherche du champ d'objet correspondant à un paramètre de référence en C#
IEnumerator<int> MyIterator() {
for (int i = 0; i < 10; i++)
yield return i;
}
Invoquer MyIterator() construit une classe avec un champ privé pour i, au lieu d'utiliser une variable de la pile.
Maintenant, pour la question: Je veux trouver un moyen de localiser le champ utilisé pour stocker la valeur de i. La raison est un peu en cause:
J'utilise des fonctions d'itération pour l'enfilage coopératif. J'écris un code simple comme celui-ci dans un itérateur, afin de suspendre l'exécution d'une fonction jusqu'à ce qu'une opération asynchrone soit terminée.
Future<string> f;
yield return file.readLine(out f);
string line = f.Result;
Lorsque ces trois instructions sont exécutées, la fonction readLine est invoquée et stocke un avenir dans le champ « f ». La fonction d'itérateur est alors suspendue, et se réveille lorsque le 'f' Futur a eu un résultat stocké dedans, à quel point je peux récupérer le résultat.
Ce que je veux être en mesure de le faire, est la suivante:
string line;
yield return file.readLine(out line);
Dans des circonstances normales, cela ne serait pas possible dans .NET, car il n'y a aucun moyen de garantir qu'une variable reste de la pile en vie assez longtemps pour que je puisse écrire. Cependant, sachant que les fonctions d'itérateur stockent tous leurs locals à l'intérieur des champs, je peux théoriquement le faire en maintenant une référence à l'itérateur (en le gardant vivant) et au champ lui-même, de sorte que lorsque l'opération readLine se termine, peut stocker le résultat directement dans le champ.
Le problème est que le CLR ne semble pas me donner un moyen de le faire. Si j'ai un paramètre 'out' ou 'ref', je peux utiliser C# non sécurisé ou MSIL brut pour convertir ce paramètre en un pointeur brut, une référence ou un TypedReference. Malheureusement, il n'est pas légal de stocker l'un de ces trois types dans un champ - même si vous trompez le compilateur en vous laissant le faire, le vérificateur de bytecode ne vous laissera pas exécuter le code. Cela est probablement dû au fait que le stockage d'une référence à une variable de pile est intrinsèquement dangereux. Donc, ce que je cherche à faire est d'écrire une fonction qui prend un paramètre out/ref, et recherche les champs de l'itérateur appelant pour trouver un champ correspondant. Une fois qu'il a le champ, il stocke une référence à l'itérateur et au champ dans un type de wrapper et associe le wrapper avec une opération asynchrone. Étant donné le temps système impliqué dans la réflexion d'exécution dans .NET, je suppose que cela n'est pas possible sans prétraiter mon code source ou post-traiter mes assemblys après la compilation.
Cependant, j'aimerais entendre quelques suggestions de solutions alternatives. :) Des idées?
Future dans l'exemple plus ou moins sert le but que vous décrivez. Avoir à dégrouper ma valeur d'un conteneur à chaque fois est une douleur, alors je voudrais sauter l'étape et écrire directement sur le terrain si je le peux. La sémantique de la modification des champs de l'itérateur me semble évidente - si vous regardez l'IL généré pour un itérateur, il est parfaitement sûr de modifier les locals aussi longtemps que vous le faites d'une manière saine. –