| Propriété | Valeur |
|---|---|
| Catégorie | Reproductibilité (JustDummies.Reproducibility) |
| Sévérité | 🔵 Info |
| Activée par défaut | Oui |
La portée de graine ambiante suit le contexte d’exécution : une portée ouverte autour d’une boucle parallèle atteint donc chaque worker — et leurs tirages s’entrelacent. Ni la séquence ni le multiensemble ne sont stables d’une exécution à l’autre : l’exécution ne rejoue rien alors qu’une graine a été épinglée.
C’est la forme que la documentation de la bibliothèque nomme elle-même, avec le remède qu’elle prescrit : une portée ouverte dans le corps de boucle donne à chaque unité de travail sa propre séquence, et toute l’exécution rejoue.
Non conforme
Parallel.For(0, 64, index => {
sut.Handle(Any.String().NonEmpty().Generate()); // JD022 : une séquence partagée, entrelacée
});
Conforme
const int runSeed = 20240501; // noté à la main : gardez-le pour rejouer, changez-le pour explorer
Parallel.For(0, 64, index => {
// une sous-graine distincte et déterministe par unité de travail, compatible netstandard2.0
using (Any.UseSeed(unchecked(runSeed * 397 ^ index))) {
sut.Handle(Any.String().NonEmpty().Generate());
}
});
Pas de correction automatique
La réparation exige une graine d’exécution que le développeur doit choisir et consigner, plus une dérivation par item à partir de la variable de boucle. Un analyseur ne peut pas inventer une graine que quelqu’un compte conserver — en générer une produirait exactement le problème de graine committée que décrit JD019.
Ce qui n’est pas signalé
- Un corps qui ouvre déjà une portée
Any.UseSeed. - Un tirage depuis un contexte isolé
Any.WithSeed(...), ou un générateur atteint par une variable locale plutôt qu’écrit en ligne depuisAny. - Une portée par item ouverte dans un utilitaire appelé par le corps — un manque à un saut que la règle accepte délibérément plutôt que de tenter une analyse interprocédurale.