Édition: Deux options illustrées ci-dessous.Quelle est la meilleure façon de renvoyer les IDisposables construits en toute sécurité?
Si vous êtes juste en utilisant la fonctionnalité qu'un IDisposable fournit, la clause bien nommée using
fonctionne très bien. Si vous êtes enveloppant un IDisposable
dans un objet, l'objet contenant lui-même doit être IDisposable
et vous devez implémenter le modèle approprié (soit une classe IDisposable
scellée, soit le messier mais standard virtual
pattern).
Mais parfois, une méthode d'usine assistée est bonne pour la propreté. Si vous renvoyez un IDisposable
directement après la construction, vous allez bien, mais si vous le construisez d'abord puis le modifiez ou exécutez un code qui peut déclencher une exception avant de revenir, vous devez appeler en toute sécurité .Dispose()
- mais seulement s'il y avait une erreur.
Par exemple, le code dangereux pourrait ressembler à ceci ...
DbCommand CreateCommandUnsafely(string commandText)
{
var newCommand = connection.CreateCommand();
newCommand.CommandText = commandText; //what if this throws?
return newCommand;
}
Solutions Deux variantes sûres suivantes ...
DbCommand CreateCommandSafelyA(string commandText)
{
DbCommand newCommand = null;
bool success = false;
try {
newCommand = connection.CreateCommand();
newCommand.CommandText = commandText; //if this throws...
success=true;
return newCommand;
} finally{
if (!success && newCommand != null)
newCommand.Dispose(); //...we'll clean up here.
}
}
DbCommand CreateCommandSafelyB(string commandText)
{
DbCommand newCommand = null;
try {
newCommand = connection.CreateCommand();
newCommand.CommandText = commandText; //if this throws...
return newCommand;
} catch {
if (newCommand != null)
newCommand.Dispose(); //...we'll clean up here.
throw;
}
}
variante A Safe est juste une ligne plus , mais semble être l'approche idiomatique. Il ne semble pas y avoir de solutions vraiment concises, bien que certaines affiches ci-dessous donnent des options utilisant le lambda qui extraient l'encapsulation de cette logique.
Le ballonnement de code avec l'une des méthodes ci-dessus reste sans danger, et est particulièrement aggravante avec le code qui ressemblait à l'origine ...
return new MyDisposableThing {
OptionA = "X",
OptionB = B.Blabla,
Values = src.Values.Where(priority => priority > 1.0),
};
Le code ci-dessus s'écrit est en toute sécurité un peu plus et moins lisible parce que vous ne pouvez plus utiliser en toute sécurité la syntaxe de setter raccourcie.
Cela ressemble à du code que je vois ailleurs. Savez-vous pourquoi c'est préférable? –
Attraper des exceptions arbitraires (comme les autres réponses semblent préconiser) provoque une variété de problèmes dans des circonstances particulières, et devrait être évité autant que possible. En plaçant la disposition à l'intérieur d'une instruction 'finally', le modèle standard évite les exceptions, et (lorsque la création de l'objet échoue) il imite le comportement de l'instruction nominale' using'. –