Sembra che l'intenzione dell'OP fosse quella di trovare un buon modello per risolvere il suo problema e risolvere il problema attuale con cui stava lottando in quel momento.
OP: "Potrei racchiudere ogni calcolo in un metodo di supporto che restituisce null in caso di errore, e quindi utilizzare semplicemente l' ??
operatore, ma esiste un modo per farlo più in generale (cioè senza dover scrivere un metodo di supporto per ogni metodo che desidero use)? Ho pensato di scrivere un metodo statico utilizzando generics che racchiude un dato metodo in un try / catch e restituisce null in caso di fallimento, ma non sono sicuro di come potrei procedere. Qualche idea? "
Ho visto molti buoni modelli che evitano i blocchi try catch annidati , pubblicati in questo feed, ma non ho trovato una soluzione al problema sopra citato. Quindi, ecco la soluzione:
Come OP menzionato sopra, voleva creare un oggetto wrapper che ritorni null
in caso di fallimento . Lo chiamerei pod ( pod a prova di eccezioni ).
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;
Ma cosa succede se si desidera creare un pod sicuro per un risultato del tipo di riferimento restituito dalle funzioni / metodi 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}";
}
Quindi, potresti notare che non è necessario "scrivere un metodo di supporto per ogni metodo che desideri utilizzare" .
I due tipi di cialde (per ValueTypeResult
s e ReferenceTypeResult
s) sono sufficienti .
Ecco il codice di SafePod
. Tuttavia non è un contenitore. Invece, crea un wrapper delegato sicuro dalle eccezioni sia per ValueTypeResult
s che per 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;
}
}
È così che puoi sfruttare l'operatore di coalescenza nullo ??
combinato con la potenza di entità cittadine di prima classedelegate
.