2009-06-03 5 views
22

J'ai couru ildasm de constater que ceci:Curieux C# en utilisant l'expansion de l'instruction

using(Simple simp = new Simple()) 
    { 
     Console.WriteLine("here"); 
    } 

génère du code IL qui est équivalent à ceci:

Simple simp = new Simple(); 
    try 
    { 
     Console.WriteLine("here"); 
    } 
    finally 
    { 
     if(simp != null) 
     { 
      simp.Dispose(); 
     } 
    } 

et la question est pourquoi diable il fait vérifier null dans le finalement? Le bloc finally ne sera exécuté que si le bloc try est exécuté, et le bloc try ne sera exécuté que si le constructeur Simple réussit (c'est-à-dire qu'il ne lance pas d'exception), auquel cas simp sera non nul. (S'il y a une certaine crainte que certaines étapes interviennent entre le constructeur Simple et le début du bloc try, alors ce serait vraiment un problème car alors une exception pourrait être lancée qui empêcherait le bloc finally de s'exécuter du tout.) Alors, pourquoi diable?

Mettant de côté (s'il vous plaît) l'argument de savoir si la déclaration à l'aide est mieux que d'essayer-enfin, je vous écris mes try-finally blocs comme:

Simple simp = new Simple(); 
    try 
    { 
     Console.WriteLine("here"); 
    } 
    finally 
    { 
     simp.Dispose(); 
     simp = null;  // sanity-check in case I touch simp again 
          // because I don't rely on all classes 
          // necessarily throwing 
          // ObjectDisposedException 
    } 
+0

Je suis curieux de savoir une chose: comment "cher" est ce contrôle supplémentaire (simp = null) par rapport à la vérification de la santé mentale générée par le compilateur, en termes de performance? En fin de compte, la différence entre les deux semble plus philosophique que pratique, mais je peux me tromper. Discussion intéressante de toute façon. –

+0

@Fredrik - Vous demandez si "set to null" est plus rapide/plus lent que "compare to null"? Je ne suis pas sûr.En dehors de cela, un avantage de l'instruction using est que vous n'avez pas à vous soucier de l'accès à cet objet en dehors de la portée d'utilisation. (À moins que vous n'ayez une autre référence.) – dss539

+22

"Pourquoi diable vérifie-t-il null dans le dernier?" Aucune bonne raison. Ignorer la vérification null est une optimisation que nous aurions pu effectuer. Nous n'avons pas. Pas vraiment une grosse affaire; Les chèques nuls sont courts et bon marché. –

Répondre

23

Non, le bloc finally sera toujours exécuté. Vous ne pouvez pas obtenir l'objet d'une nouvelle mais d'une autre fonction qui renvoie votre objet - et il pourrait renvoyer NULL. using() est votre ami!

dss539 a eu la gentillesse de proposer sa note j'inclure:

using(Simple simp = null) 

est encore une autre raison pour que l'expansion doit vérifier nulle première.

+2

Juste pour développer un peu, vous pourrait utiliser le motif d'usine et faire quelque chose comme using (myFactory.CreateMyObject()) qui pourrait aboutir à un objet nul. –

+8

Si le constructeur échoue (avant l'essai), le dernier ne sera pas exécuté. – Zifre

+9

L'hypothèse est que le 'nouveau' lèvera une exception s'il échoue, donc ne retournera jamais un Null. S'il lance l'exception, le bloc try n'est même pas atteint et le bloc finally n'est pas exécuté. Donc, le bloc finally n'est PAS TOUJOURS exécuté. – spoulson

10

using(Simple simp = null) est encore un autre raison que l'expansion doit d'abord vérifier null.

+0

Pourquoi cela compile-t-il même? Il est garanti qu'il s'agit d'une erreur d'exécution. –

+2

Une erreur d'exécution est garantie uniquement si vous utilisez l'objet simp. Certes, il n'y a pas de raison pour l'utiliser si vous ne le faites pas .... –

+1

Et bien qu'il soit peu probable que vous ayez codé = null explicitement, cela pourrait être = SomeFactoryMethod() qui pourrait retourner null. – n8wrl

4

MSDN dans l'instruction using.

Ce que je pense est étrange est qu'il ne se développe pas:

Simple simp = new Simple(); 
Simple __compilergeneratedtmpname = simp; 
try 
{ 
    Console.WriteLine("here"); 
} 
finally 
{ 
    if(__compilergeneratedtmpname != null) 
    { 
     __compilergeneratedtmpname.Dispose(); 
    } 
} 
+0

Vous aimeriez pouvoir changer la variable 'simp' dans le bloc using ?? –

+0

Je ne me suis pas rendu compte que le compilateur impose simp en tant que readonly. – Dolphin

+3

Dolphin: Cela causerait des problèmes lors du multithreading. Lorsque le constructeur est immédiatement suivi par l'essai, le CLR garantit qu'aucun thread ne «interrompra» au milieu. Votre chemin, il n'y a aucune garantie. – configurator

1

Il semble que votre commentaire:

«S'il y a une certaine crainte que certaines étapes intermédiaires pourraient venir entre le simple constructeur et le début du bloc try, alors ce serait vraiment un problème car alors une exception pourrait être lancée qui empêcherait le bloc finally de s'exécuter du tout. "

est peut-être mort. Voir:

Atomicity & Asynchronous Exception Failures

Je veux également souligner la question (s) avec WCF et en utilisant:

Avoiding Problems with the Using Statement and WCF Service Proxies dont les références:

Avoiding Problems with the Using Statement

Questions connexes