2016-10-26 1 views
2

Je suis tombé par hasard sur un étrange problème en C# /. Net 4.5.2 ici ...Ordre d'évaluation pour Debug.Assert (condition, interpolatedStringMessage) en .Net 4.5.2

J'ai eu un chèque !ContainsKey() dans un Debug.Assert() avec une chaîne interpolée contenant la valeur, se trouve, comme si

var dict = new Dictionary<string, string>(); 
string invalidKey = "invalidKey"; 

try 
{ 
    Debug.Assert((!dict.ContainsKey(invalidKey)), $"{nameof(dict)} contains the key {dict[invalidKey]}!"); 
} 
catch (KeyNotFoundException) 
{ 
    Console.WriteLine("AssertionString was interpolated before the check was performed!"); 
} 

(seulement sans le bloc try-catch dans ma première version)

je l'aurais prévu de ne jamais courir dans la catch il , comme c'est aussi le cas quand e. g. vérifier le code .NETFiddle (voir https://dotnetfiddle.net/RyQooW), cependant, dans Visual Studio, exécutant le code sous .Net 4.5.2, je reçois le texte suivant:

interpolated string evaluated before assertion condition

Il est certainement l'interpolation de chaîne (et non problème avec ContainsKey), car lorsque je supprime l'interpolation du message, l'exception n'est pas levée.

L'ordre d'évaluation n'est-il pas garanti? Je pensais que la chaîne ne serait pas interpolée si l'assertion était vraie.

Ou est-ce juste une erreur dans cette version du Runtime .Net (peut-être une optimisation pour interpoler les chaînes d'assertion tôt pour les rendre statiques ou quelque chose?)

Merci pour toute entrée et cheers!

+0

Quelle version .NET utilisez-vous? Je n'ai aucune exception avec v4.6.1. – dymanoid

+0

@dymanoid 4.6.1 aussi, sur un système Windows 8.1 avec VS 2015 Update 3 –

Répondre

2

Bon, alors pour tout le monde qui trébuche sur cela aussi:

Il est pas un bug, apparemment, mais en fait tout simplement Debug.Assert étant une méthode régulière (pas une aide spéciale intégrée, etc., voir aussi Microsoft's reference source), et donc tout argument de chaîne a pour être interpolé avant entrant dans la méthode (puisque les objets en question pourraient ne pas être disponibles en dehors de la portée d'appel).
Mais puisque tant le conditionnel et le message sont des arguments de méthode et l'évaluation de l'état se produit intérieur la la méthode (et pas l'appel de méthode à la suite de l'évaluation), le KeyNotFoundException ci-dessus est bien sûr jeté avant que la méthode est entrée et la condition vérifiée.

La seule chose qui fait Debug.Assert sorte de spécial est qu'il a un attribut [System.Diagnostics.Conditional("DEBUG")] et est donc supprimé dans les versions non-Debug.

C'est donc en fait un comportement entièrement compréhensible et correct (en toute version dot net), bien que ce soit un piège.

+1

Oui, c'est exactement comme ça. C'est une méthode régulière, donc toutes les valeurs des paramètres sont calculées avant l'appel. Par conséquent, l'expression du paramètre # 2 déclenche immédiatement une exception. La seule façon de forcer l'autre comportement est d'écrire une méthode 'Assert' qui prend un 'generator' comme' (bool, Func ) 'qui serait appelé si nécessaire et ressemblerait à ceci: 'Affirmer (! Dict.contient (x),() => $ "Yep, dict contient {dict [x]}"); cependant, votre message d'affirmation a l'air vraiment étrange. J'écrirais personnellement 'Assert (! Dict.contains (x), $" ouops il contenait la clé {x} ")' car il dit KEY – quetzalcoatl

+0

@quetzalcoatl Uhhh, ouais, je pourrais avoir brouillé cela en essayant de généraliser pour l'afficher sur SO m) Pensez-vous qu'il serait nécessaire de le réparer (et donc de réécrire la question en l'éditant?) –

+0

Vous avez posé la question, et même donné une bonne réponse, donc je pense, Laisse le rester. Je pensais juste que vous pourriez avoir oublié cette faute de frappe qui était la cause réelle d'exception :) Je pense que c'est bien de le laisser tel quel, acclamations. – quetzalcoatl