Parece que la intención del OP era encontrar un buen patrón para resolver su problema y resolver el problema actual con el que estaba luchando en ese momento.
OP: "Podría envolver cada cálculo en un método auxiliar que devuelve nulo en caso de falla, y luego simplemente usar el ??
operador, pero ¿hay alguna forma de hacer esto de manera más general (es decir, sin tener que escribir un método auxiliar para cada método que quiero uso)? He pensado en escribir un método estático usando genéricos que envuelva cualquier método dado en un try / catch y devuelva nulo en caso de falla, pero no estoy seguro de cómo lo haría. ¿Alguna idea? "
Vi muchos patrones buenos que evitan bloques de captura de prueba anidados , publicados en esta fuente, pero no encontré una solución al problema que se cita anteriormente. Entonces, aquí está la solución:
Como OP mencionó anteriormente, quería hacer un objeto contenedor que regrese null
en caso de falla . Yo lo llamaría una cápsula ( cápsula segura para excepciones ).
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;
Pero, ¿qué sucede si desea crear un pod seguro para un resultado de Tipo de referencia devuelto por las funciones / métodos de 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}";
}
Por tanto, es posible que observe que no es necesario "escribir un método auxiliar para cada método que desee utilizar" .
Los dos tipos de vainas (para ValueTypeResult
sy ReferenceTypeResult
s) son suficientes .
Aquí está el código de SafePod
. Sin embargo, no es un contenedor. En su lugar, crea un contenedor delegado seguro para excepciones tanto para ValueTypeResult
s como para ReferenceTypeResult
s.
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;
}
}
Así es como puede aprovechar el operador de fusión nula ??
combinado con el poder de las entidades ciudadanas de primera clasedelegate
.