Parece que a intenção do OP era encontrar um bom padrão para resolver seu problema e resolver o problema atual com o qual ele estava lutando naquele momento.
OP: "Eu poderia envolver cada cálculo em um método auxiliar que retorna nulo em caso de falha e, em seguida, apenas usar o ??
operador, mas há uma maneira de fazer isso de forma mais geral (ou seja, sem ter que escrever um método auxiliar para cada método que desejo usar)? Já pensei em escrever um método estático usando genéricos que envolva qualquer método em um try / catch e retorne null em caso de falha, mas não tenho certeza de como faria isso. Alguma idéia? "
Eu vi muitos bons padrões que evitam blocos aninhados de try catch , postados neste feed, mas não encontrei uma solução para o problema citado acima. Então, aqui está a solução:
Como OP mencionou acima, ele queria fazer um objeto wrapper que retornasse null
em caso de falha . Eu o chamaria de pod ( pod seguro para exceções ).
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;
Mas e se você quiser criar um pod seguro para um resultado de Tipo de Referência retornado por funções / métodos 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}";
}
Portanto, você pode notar que não há necessidade de "escrever um método auxiliar para cada método que deseja usar" .
Os dois tipos de pods (para ValueTypeResult
s e ReferenceTypeResult
s) são suficientes .
Aqui está o código de SafePod
. Porém, não é um contêiner. Em vez disso, ele cria um wrapper de delegado seguro para exceções para ValueTypeResult
s e 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;
}
}
É assim que você pode alavancar o operador de coalescência nula ??
combinado com o poder das entidades de cidadãos de primeira classedelegate
.