165 votes

Pourquoi aren ' t variables déclarées dans « essayer » figurant dans « catch » ou « enfin » ?

En C# et en Java (et éventuellement d'autres langues), les variables déclarées dans un "try" bloc ne sont pas dans la portée de la "capture" ou "enfin" blocs. Par exemple, le code suivant ne compile pas:

try {
  String s = "test";
  // (more code...)
}
catch {
  Console.Out.WriteLine(s);  //Java fans: think "System.out.println" here instead
}

Dans ce code, une erreur de compilation se produit sur la référence à s dans le bloc catch, parce que s est uniquement dans la portée dans le bloc try. (En Java, l'erreur de compilation est "s ne peut pas être résolu"; en C#, c'est "Le nom de 's' n'existe pas dans le contexte actuel".)

La solution générale à ce problème semble être au lieu de déclarer des variables juste avant le bloc try, au lieu de dans le bloc try:

String s;
try {
  s = "test";
  // (more code...)
}
catch {
  Console.Out.WriteLine(s);  //Java fans: think "System.out.println" here instead
}

Cependant, au moins pour moi, (1) cela se sent comme un maladroit solution, et (2) les résultats dans les variables ayant un champ d'application plus large que le programmeur (comme l'ensemble du reste de la méthode, au lieu d'uniquement dans le contexte de la try-catch-finally).

Ma question est, quelles ont été/sont la raison d'être(s) à l'arrière de la langue de conception de décision (en Java, en C#, et/ou dans toutes autres langues)?

191voto

John Christensen Points 3390

Deux choses:

  1. Généralement, JavaScript a seulement 2 niveaux de portée mondiale et de la fonction. Mais, try/catch est une exception (pas de punn prévu). Lorsqu'une exception est levée et l'objet de l'exception devient une variable est affectée, que l'objet de la variable n'est disponible que dans la "capture" de la section et est détruit dès que la capture est terminée.

  2. (et plus important). Vous ne pouvez pas savoir où dans le bloc try la levée de l'exception. Il peut avoir été avant votre variable a été déclarée. Par conséquent, il est impossible de dire quelles variables seront disponibles pour le catch/finally clause. Considérons le cas suivant, où la portée est comme vous l'avez suggéré:


    try
    {
        throw new ArgumentException("some operation that throws an exception");
        string s = "blah";
    }
    catch (e as ArgumentException)
    {  
        Console.Out.WriteLine(s);
    }
 

C'est clairement un problème lorsque vous atteignez le gestionnaire d'exception, s n'auront pas été déclarés. Étant donné que les captures sont conçu pour traiter des circonstances exceptionnelles et finallys doit exécuter, sans danger et déclarer ce un problème au moment de la compilation est bien meilleure que lors de l'exécution.

56voto

Burkhard Points 6734

Comment pourriez-vous être sûr, que vous atteint la partie déclaration dans votre bloc catch ? Que se passe-t-il si l’instanciation lève l’exception ?

22voto

Ferruccio Points 51508

Traditionnellement, dans le C-style de langues, ce qui se passe à l'intérieur des accolades reste à l'intérieur des accolades. Je pense que le fait d'avoir la durée de vie d'une variable s'étendent à travers les étendues comme ça serait pas intuitif pour la plupart des programmeurs. Vous pouvez réaliser ce que vous voulez, en enfermant le try/catch/finally blocs à l'intérieur d'un autre niveau de croisillons. par exemple

... code ...
{
    string s = "test";
    try
    {
        // more code
    }
    catch(...)
    {
        Console.Out.WriteLine(s);
    }
}

EDIT: je pense que pour chaque règle n'est qu'une exception. Ce qui suit est valable en C++:

int f() { return 0; }

void main() 
{
    int y = 0;

    if (int x = f())
    {
        cout << x;
    }
    else
    {
        cout << x;
    }
}

Le champ d'application de x est la condition, la clause et la clause else.

11voto

John Rudy Points 16436

Tout le monde a été jusqu'à la base-ce qui se passe dans un bloc de séjours dans un bloc. Mais dans le cas d' .NET, il peut être utile d'examiner ce que le compilateur qui se passe. Prenez, par exemple, le try/catch code (notez que le StreamReader est déclaré, à juste titre, en dehors des blocs):

static void TryCatchFinally()
{
    StreamReader sr = null;
    try
    {
        sr = new StreamReader(path);
        Console.WriteLine(sr.ReadToEnd());
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
    finally
    {
        if (sr != null)
        {
            sr.Close();
        }
    }
}

Cela permettra de compiler quelque chose de similaire à la suivante en MSIL:

.method private hidebysig static void  TryCatchFinallyDispose() cil managed
{
  // Code size       53 (0x35)    
  .maxstack  2    
  .locals init ([0] class [mscorlib]System.IO.StreamReader sr,    
           [1] class [mscorlib]System.Exception ex)    
  IL_0000:  ldnull    
  IL_0001:  stloc.0    
  .try    
  {    
    .try    
    {    
      IL_0002:  ldsfld     string UsingTest.Class1::path    
      IL_0007:  newobj     instance void [mscorlib]System.IO.StreamReader::.ctor(string)    
      IL_000c:  stloc.0    
      IL_000d:  ldloc.0    
      IL_000e:  callvirt   instance string [mscorlib]System.IO.TextReader::ReadToEnd()
      IL_0013:  call       void [mscorlib]System.Console::WriteLine(string)    
      IL_0018:  leave.s    IL_0028
    }  // end .try
    catch [mscorlib]System.Exception 
    {
      IL_001a:  stloc.1
      IL_001b:  ldloc.1    
      IL_001c:  callvirt   instance string [mscorlib]System.Exception::ToString()    
      IL_0021:  call       void [mscorlib]System.Console::WriteLine(string)    
      IL_0026:  leave.s    IL_0028    
    }  // end handler    
    IL_0028:  leave.s    IL_0034    
  }  // end .try    
  finally    
  {    
    IL_002a:  ldloc.0    
    IL_002b:  brfalse.s  IL_0033    
    IL_002d:  ldloc.0    
    IL_002e:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()    
    IL_0033:  endfinally    
  }  // end handler    
  IL_0034:  ret    
} // end of method Class1::TryCatchFinallyDispose

Que voyons-nous? MSIL respecte les blocs, ils sont intrinsèquement partie de la sous-tendent le code généré lors de la compilation du C#. La portée n'est pas seulement difficile dans le C# spec, c'est dans le CLR et CLS spec.

Le champ d'application vous protège, mais vous le faites à l'occasion des travaux autour d'elle. Au fil du temps, vous vous habituez à elle, et elle commence à se sentir naturel. Comme tout le monde l'a dit, ce qui se passe dans un bloc de séjours dans ce bloc. Vous voulez partager quelque chose? Vous devez aller à l'extérieur les blocs ...

9voto

ravenspoint Points 8840

En C++, en tout cas, la portée d’une variable automatique est limitée par les accolades qui l’entourent. Pourquoi tout le monde c’est différent de plunking à un mot-clé essayer dehors les accolades attendriez-vous ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X