2011-08-31 2 views
4

Considérez ce qui suit:Jeter et le retour des valeurs dans .NET

 TextReader reader = new StreamReader(file); 
     XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 
     return (T)xmlSerializer.Deserialize(reader); 

Et

 using (TextReader reader = new StreamReader(file)) 
     { 
      XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 
      return (T)xmlSerializer.Deserialize(reader); 
     } 

ce qui va réellement se produire dans ce dernier morceau de code? Est-ce que le Dispose() sera appelé?

+2

Oui dispose sera appelé. –

Répondre

1

La ressource de l'instruction using, reader sera éliminée lorsque la portée d'utilisation se termine. En cas vous êtes c'est lorsque le résultat de la désérialisation a été casté en T.

vous pourriez vous étendre êtes code à la (à peu près) équivalent ci-dessous:

TextReader reader = null; 
try{ 
    reader = new StreamReader(file); 
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 
    var obj = xmlSerializer.Deserialize(reader); 
    T returnVal = (T)obj; 
    return returnVal; 
} finally{ 
    reader.Dispose(); 
} 

dans cette version, il devient clair que le dernier lecteur utilisé est bien avant l'instruction de retour.

Si vous deviez retourner le lecteur, vous auriez des problèmes car l'objet renvoyé serait éliminé et donc inutilisable.

EDIT: L'IL du code ci-dessus est:

IL_0000: nop 
    IL_0001: ldnull 
    IL_0002: stloc.0 
    .try 
    { 
    IL_0003: nop 
    IL_0004: ldstr  "" 
    IL_0009: newobj  instance void [mscorlib]System.IO.StreamReader::.ctor(string) 
    IL_000e: stloc.0 
    IL_000f: ldtoken !!T 
    IL_0014: call  class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 
    IL_0019: newobj  instance void [System.Xml]System.Xml.Serialization.XmlSerializer::.ctor(class [mscorlib]System.Type) 
    IL_001e: stloc.1 
    IL_001f: ldloc.1 
    IL_0020: ldloc.0 
    IL_0021: callvirt instance object [System.Xml]System.Xml.Serialization.XmlSerializer::Deserialize(class [mscorlib]System.IO.TextReader) 
    IL_0026: stloc.2 
    IL_0027: ldloc.2 
    IL_0028: unbox.any !!T 
    IL_002d: stloc.3 
    IL_002e: ldloc.3 
    IL_002f: stloc.s CS$1$0000 
    IL_0031: leave.s IL_003d 
    } // end .try 
    finally 
    { 
    IL_0033: nop 
    IL_0034: ldloc.0 
    IL_0035: callvirt instance void [mscorlib]System.IO.TextReader::Dispose() 
    IL_003a: nop 
    IL_003b: nop 
    IL_003c: endfinally 
    } // end handler 
    IL_003d: nop 
    IL_003e: ldloc.s CS$1$0000 
    IL_0040: ret 
} // end of method 

la chose à noter est que CS 1 $ 0000 qui est la valeur de retour est poussée à la pile juste avant la seule instruction RET. Donc l'ordre d'exécution est différent de ce à quoi il ressemble dans le code C#. En outre, il vaut la peine de noter les commandes stloc.s CS $ 1 $ 0000 et leave.s qui stockent la valeur de retour suivie par l'un des GOTO glorifiés. leave.s quitte l'essai et saute à l'étiquette IL_003d, juste avant de pousser la valeur de retour à la pile

+0

Est-ce que 'return' ne quitte pas la portée, sans jamais invoquer le Dispose()? – kasperhj

+0

@lejon le bloc finally sera toujours exécuté. En réalité, il n'y a jamais que le point de retour d'une méthode en regardant l'IL. plusieurs déclarations de retour et enfin les blocs sont fondamentalement glorifiés GOTOs –

+0

Donc, la réponse d'Amby est plus proche de l'IL que vous mentionnez? – kasperhj

3

Oui, le Dispose sera appelé.

+0

J'ajouterai qu'il y a probablement quelques petites mises en garde: http://stackoverflow.com/questions/3216046/does-the-c-finally-block-always-execute/3216078#3216078 Mais ce sont des situations très exceptionnelles, donc l'OP peut les ignorer. – xanatos

4

Oui, il sera appelé.

La déclaration using est le sucre syntaxique pour:

try 
{ 
    // Do stuff 
    return; 
} 
finally 
{ 
    // Dispose 
} 

et le finally sera appelé même sur le return.

Vous pouvez donc l'utiliser en toute sécurité.

1

Oui, c'est ainsi qu'il sera converti en IL.

TextReader reader; 
    T returnValue; 
    try{ 
     reader = new StreamReader(file)); 
     XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); 
     var obj = xmlSerializer.Deserialize(reader); 
     returnVal = (T)obj; 
    } 
    finally{   
     reader.Dispose(); 
     return returnVal;    
    } 
+0

Cela diffère des autres réponses, bien qu'il soit plus logique que le retour soit terminé en dernier, comme je m'y attendais. Est-il certain que c'est comme ça, et les autres réponses cachent-elles cette vérité? – kasperhj

+0

J'adore le fait que vous n'avez même pas pris la peine de renommer les variables lorsque vous avez copié-collé. votre code ne compile pas cependant –

Questions connexes