Y a-t-il un meilleur moyen d'écrire ce code sans utiliser goto code>? Cela semble maladroit, mais je ne peux pas penser à une meilleure façon. Je dois pouvoir effectuer une tentative de nouvelle tentative, mais je ne veux pas dupliquer de code.
public void Write(string body)
{
bool retry = false;
RetryPoint:
try
{
m_Outputfile.Write(body);
m_Outputfile.Flush();
}
catch (Exception)
{
if( retry )
throw;
// try to re-open the file...
m_Outputfile = new StreamWriter(m_Filepath, true);
retry = true;
goto RetryPoint;
}
}
7 Réponses :
Voici la logique de base que j'utiliserais au lieu d'une instruction GOTO:
bool succeeded = false; int tries = 2; do { try { m_Outputfile = new StreamWriter(m_Filepath, true); m_Outputfile.Write(body); m_Outputfile.Flush(); succeeded = true; } catch(Exception) { tries--; } } while (!succeeded && tries > 0);
Cela effectue des essais infinis, utilise la syntaxe C ++ pour la capture et ne réthore pas. Je ne sais pas pourquoi il a été évoqué quand il est clairement faux.
Oh, et ça ne rouvre pas sur l'échec.
@Steven Sudit: Le corps du bloc de capture est impliqué comme le code d'origine.
@Lbushkin, pas sans quelques changements. Le bloc de capture d'origine ne compilera même pas dans ce code. Dans tous les cas, cela tente toujours pour toujours, ce qui est pas i> l'objectif.
Le bloc de capture doit être Fermer Code> Le Streamwriter!
Merci de réparer la syntaxe de bloc de capture, mais je ne vois pas pourquoi un faire code> /
tandis que code> serait préférable à un
pour code> /
Break code>. Il nécessite une variable supplémentaire et plus de lignes de code.
Hmm, cela ne réthore pas sur la dernière échec, non plus. Je suis désolé, mais peu importe la façon dont je le regarde, même la nouvelle version de cette réponse a plusieurs erreurs. C'était une erreur de la marquer acceptée.
Et si vous le mettez dans une boucle? Quelque chose de similaire à cela, peut-être.
while(tryToOpenFile) { try { //some code } catch { } finally { //set tryToOpenFile to false when you need to break } }
Cela a des tentatives infinies et ne résout généralement pas le problème.
Ouais, c'est pourquoi j'ai dit quelque chose de similaire i>. L'idée était de le faire dans une boucle, ce qui semble être l'idée générale dans chaque réponse ici.
Droite, en utilisant une boucle est assez évidente (bien que non universelle). Le diable est dans les détails.
Essayez quelque chose comme ce qui suit:
int tryCount = 0; bool succeeded = false; while(!succeeded && tryCount<2){ tryCount++; try{ //interesting stuff here that may fail. succeeded=true; } catch { } }
Le bool n'est pas nécessaire. Plus au point, cela ne jette pas le dernier échec.
public void Write(string body, bool retryOnError) { try { m_Outputfile.Write(body); m_Outputfile.Flush(); } catch (Exception) { if(!retryOnError) throw; // try to re-open the file... m_Outputfile = new StreamWriter(m_Filepath, true); Write(body, false); } }
@Steven: Bien sûr, pourquoi pas? C'est une récursion de queue simple et très lisible: écriture (corps, false) code> documente assez clairement l'intention de l'écrivain (essayez de nouveau mais ne réessayez pas) et cela évite d'encombrer le code (pas de boucle, non < code> réussi code> variable). Remplacement de
RetryonError Code> avec un décompte de tentative est également un exercice facile ...
Quelques raisons. En raison de la façon dont les travaux de GC, la récursion conserve des objets vivants plus longtemps, il convient de l'éviter quand une solution itérative est suffisamment claire. Lorsque l'optimisation de la récursion de la queue est possible, cela est moins problématique. Cet exemple ne recouvre pas plus d'une fois, mais vous modifiez le BOOL à un compte à rebours de réessayer si vous souhaitez augmenter cela (et vous le feriez probablement). Un tel changement agrandirait les effets.
Mais de côté de cette question d'optimisation, ma principale préoccupation est que ce problème ne se prête pas particulièrement bien à une solution récursive. Le code va donc être plus difficile à comprendre et à entretenir. La version itérative rend sa boucle explicite au lieu d'attendre que le lecteur remarque que la troisième "écriture" qu'ils voient est en réalité un appel récursif. Un drapeau de bool n'est pas nécessaire, peu importe, car le compte à rebours des tentatives sert également d'indicateur de quand nous devrions retirer.
@Steven: True, ce n'est certainement pas l'un de ces exemples où récursives bat l'itération (et +1 à votre solution itérative sans le drapeau, BTW). Je considère toujours une alternative valide (et espérons instructive), donc je ne supprimerai pas la réponse. Un avantage que je vois dans la solution récursive est que le code de manipulation des erreurs est complètement à l'intérieur de la clause de capture (à l'exception du paramètre supplémentaire), le code représente donc le flux naturel (non exceptionnel) i> de la programme. La solution itérative, d'autre part, commence par une grande boucle qui n'est utilisée que dans des circonstances exceptionnelles i> exceptionnelles.
Je ne suggère pas de supprimer votre réponse! C'est un peu comme celui de Lbushkin, en ce sens qu'il représente une approche unique qui est précieuse sur le plan pédagogique, même si ce n'est pas nécessairement le meilleur convivialité pour le problème. Ma critique est destinée à être entièrement constructive. Bon point de garder le code exceptionnel dans le gestionnaire d'exception. Mon Fancy i> répondez à cela, ce que j'ai réellement utilisé en production, est d'utiliser un retrie de délégué, comme celui de Lbushkin. Cela dissimule les tripes de la nouvelle tentative très proprement et permet de filtrer les types d'exception fatale (Stackoverflow, Filhabort, etc.).
La solution de Michael ne remplit pas tout à fait les exigences, qui doivent réessayer un nombre fixe de fois, jetant la dernière échec.
Pour cela, je recommanderais une boucle simple, en comptant. Si vous réussissez, sortez avec une pause (ou, si cela convient, retour). Sinon, laissez le chèque de capture pour voir si l'index est réduit à 0. Si oui, retire au lieu de la journalisation ou de l'ignorer. P> dans l'exemple ci-dessus, un retour aurait été bien, mais je voulais montrer l'affaire générale. p> p>
@Anthony: Merci, c'était une erreur. Je vais le réparer immédiatement.
Je viens de faire un changement de plus que je pense s'appliquer à n'importe quelle solution: je l'ai fait Fermer Code> L'ancien brancelet avant d'ouvrir un nouveau. Si cela n'est pas terminé, l'ancienne garderait une poignée ouverte jusqu'à ce que GC commence, bloquez de nouveaux de travailler!
@ Réponse de Michael (avec Un bloc de capture correctement mis en œuvre) est probablement le plus facile à utiliser dans votre cas et est le plus simple. Mais dans l'intérêt de présenter des alternatives, voici une version qui facète le contrôle de flux "réessayer" dans une méthode distincte: vous pouvez, bien sûr, améliorer cela avec des choses comme des comptes de tentative, meilleure propagation d'erreur, et ainsi de suite. p> p>
Je ne sais vraiment pas quoi faire de cela. Sans doute, ce type de solution axée sur les délégués est élégant, flexible et réutilisable. Là encore, il ne fait pas actuellement ce qui est nécessaire. En fin de compte, je ne vais ni up- ni baisse vote.
@Steven: Est-ce que quelque chose m'a manqué? De quelle manière ne répond pas aux exigences?
Les voies que vous avez mentionnées: N'essayez pas de décompte, ne jetez pas le dernier essai, etc. Nul doute que vous pouvez faire i> cela fait ce qui est nécessaire - j'ai vu assez de vos réponses à Sois certain de cela - mais tu n'as pas à ce moment-là.
@Steven: Eh bien, dans toute l'équité, l'OP n'a jamais mentionné une nécessité d'effectuer plusieurs tentatives et, en fait, comme le code d'origine est écrit, il ne réalisera qu'une nouvelle tentative une fois. En ce qui concerne la dernière tentative, le code devrait le faire, car le délégué de l'action est invoqué dans le bloc de capture.
Si tout ce que nous voulons faire est de réessayer une fois, nous pouvons sauter tous les délégués et les boucles, plutôt le codage fort. J'espère que nous pourrions faire mieux que ce type de solution de force brute, en particulier de la façon dont il échoue pour un grand nombre de tentatives.
@Heinzi, je vais facilement accorder qu'il est à la fois créatif et techniquement sophistiqué. Je ne sais tout simplement pas si c'est approprié.
@Lbuskkin, on m'a rappelé que j'avais réellement utilisé le code qui est un peu comme le vôtre. J'avais une méthode d'appel réessaye qui a pris un délégué d'action et un compte de tentative. Cela ignorerait les premiers échecs N-1, réessayant le code dans le délégué. Meilleur des deux mondes, vraiment.
@Steven: True, mais même si ce n'est peut-être pas la meilleure solution pour le problème exact de l'OP, c'est certainement une réponse utile et instructive, d'où la uppote.
@Heinzi, oui, je l'ai éventuellement évoqué, et toi.
avec un booléen
Cela ne peut pas travailler possible. Pour une chose, NotFaileDONCE =! NotFaileDONCE) code> sera toujours faux, alors ça ne jettera jamais. Au lieu de cela, ça va boucler pour toujours.
@Steve, regarde à nouveau le code, ce n'est pas un chèque d'égalité, mais une mission.
C'est intelligent, ce qui signifie mauvais. Le compilateur générera des avertissements pour cela, et cela devrait, puisqu'une cession au milieu d'un prédicat est probablement due à une faute de frappe. De même, il est intelligent / mauvais de fixer un bool sur False en le notant au lieu de, vous savez, la fixant à FALSE. Ce code est inutilement obscurci.
@ Steven- d'accord avec le point de mission dans l'IF (), j'ai changé cela. Cependant, noter est complètement différent de la définir à False. C'est le notant qui va s'assurer que cela ne fonctionne qu'une seule fois.
Merci d'avoir déplacé l'affectation du prédicat. Si vous regardez le flux, vous verrez que vous auriez pu avoir un hasfailed code>, à la place. Il serait fixé au-dessus de la ligne qui instancie un streamwriter. Beaucoup plus simple de cette façon. En outre, il n'y a pas besoin d'un autre (ou de toutes ces bretelles), comme un lancer sort du flux de courant.
Désolé, je ne peux pas résister! xkcd.com/292
Il y a toujours une meilleure façon d'écrire la logique sans goto.
@Mcwafflestix: Je ne suis pas d'accord. Il y a des cas très rares i> cas où utiliser
goto code> en fait des rendements de code de nettoyage - la rupture des boucles imbriquées est un exemple couramment cité (puisque c # n'a pas marqué de pauses comme Java ). Voir Stackoverflow.com/Questtions/2542289/... Pour plus.
@Heinzi: D'accord, votre point est pris: pour certaines conditions de code très mauvais, l'utilisation de GOTO peut donner un code de nettoyage; Je classerais cela comme une odeur, cependant, et un particulièrement mauvais.
@Mcwafflestix: Et un peu répandu. C'est généralement un indicateur de profondeur de nidification excessive dans une méthode.