Il semble que l'intention du PO était de trouver un bon modèle pour résoudre son problème et résoudre le problème actuel avec lequel il luttait à ce moment-là.
OP: "Je pourrais encapsuler chaque calcul dans une méthode d'assistance qui renvoie null en cas d'échec, puis utiliser simplement l' ??opérateur, mais y a-t-il un moyen de le faire plus généralement (c'est-à-dire sans avoir à écrire une méthode d'assistance pour chaque méthode que je veux use)? J'ai pensé à écrire une méthode statique utilisant des génériques qui encapsule une méthode donnée dans un try / catch et renvoie null en cas d'échec, mais je ne suis pas sûr de la manière dont je procéderais. Des idées? "
J'ai vu beaucoup de bons modèles qui évitent les blocs try catch imbriqués , publiés dans ce flux, mais n'ai pas trouvé de solution au problème cité ci-dessus. Alors, voici la solution:
Comme OP mentionné ci-dessus, il voulait créer un objet wrapper qui retourne nullen cas d'échec . Je l'appellerais un pod ( pod sans danger pour les exceptions ).
public static void Run()
{
// The general case
// var safePod1 = SafePod.CreateForValueTypeResult(() => CalcX(5, "abc", obj));
// var safePod2 = SafePod.CreateForValueTypeResult(() => CalcY("abc", obj));
// var safePod3 = SafePod.CreateForValueTypeResult(() => CalcZ());
// If you have parameterless functions/methods, you could simplify it to:
var safePod1 = SafePod.CreateForValueTypeResult(Calc1);
var safePod2 = SafePod.CreateForValueTypeResult(Calc2);
var safePod3 = SafePod.CreateForValueTypeResult(Calc3);
var w = safePod1() ??
safePod2() ??
safePod3() ??
throw new NoCalcsWorkedException(); // I've tested it on C# 7.2
Console.Out.WriteLine($"result = {w}"); // w = 2.000001
}
private static double Calc1() => throw new Exception("Intentionally thrown exception");
private static double Calc2() => 2.000001;
private static double Calc3() => 3.000001;
Mais que faire si vous souhaitez créer un pod sécurisé pour un résultat de type de référence retourné par les fonctions / méthodes CalcN ().
public static void Run()
{
var safePod1 = SafePod.CreateForReferenceTypeResult(Calc1);
var safePod2 = SafePod.CreateForReferenceTypeResult(Calc2);
var safePod3 = SafePod.CreateForReferenceTypeResult(Calc3);
User w = safePod1() ?? safePod2() ?? safePod3();
if (w == null) throw new NoCalcsWorkedException();
Console.Out.WriteLine($"The user object is {{{w}}}"); // The user object is {Name: Mike}
}
private static User Calc1() => throw new Exception("Intentionally thrown exception");
private static User Calc2() => new User { Name = "Mike" };
private static User Calc3() => new User { Name = "Alex" };
class User
{
public string Name { get; set; }
public override string ToString() => $"{nameof(Name)}: {Name}";
}
Ainsi, vous remarquerez peut-être qu'il n'est pas nécessaire "d'écrire une méthode d'assistance pour chaque méthode que vous souhaitez utiliser" .
Les deux types de pods (pour ValueTypeResults et ReferenceTypeResults) suffisent .
Voici le code de SafePod. Ce n'est cependant pas un conteneur. Au lieu de cela, il crée un wrapper de délégué sécurisé pour les exceptions pour ValueTypeResults et ReferenceTypeResults.
public static class SafePod
{
public static Func<TResult?> CreateForValueTypeResult<TResult>(Func<TResult> jobUnit) where TResult : struct
{
Func<TResult?> wrapperFunc = () =>
{
try { return jobUnit.Invoke(); } catch { return null; }
};
return wrapperFunc;
}
public static Func<TResult> CreateForReferenceTypeResult<TResult>(Func<TResult> jobUnit) where TResult : class
{
Func<TResult> wrapperFunc = () =>
{
try { return jobUnit.Invoke(); } catch { return null; }
};
return wrapperFunc;
}
}
C'est ainsi que vous pouvez tirer parti de l'opérateur de fusion nul ??combiné à la puissance d' entités citoyennes de premier ordredelegate .