Схоже, що наміром ОП було знайти вдалу модель для вирішення його питання та вирішення поточної проблеми, з якою він на той момент боровся.
ОП: "Я міг би обернути кожен обчислення в допоміжний метод, який повертає нуль після відмови, а потім просто скористатися ??
оператором, але чи є спосіб зробити це більш загально (тобто без необхідності писати допоміжний метод для кожного методу, який я хочу ?) Я думав над написанням статичного методу з використанням дженериків, який перетворює будь-який заданий метод на спробу / ловити і повертає нуль при відмові, але я не впевнений, як би я з цим пішов. Будь-які ідеї? "
Я побачив багато хороших зразків, які уникають вкладених блоків спробу вловлювання , розміщених у цьому каналі, але не знайшли рішення проблеми, про яку йдеться вище. Отже, ось рішення:
Як згадувалося в ОП, він хотів зробити обгортковий об'єкт, який повертається null
після відмови . Я б назвав це стручком ( безпечний для винятку стручок ).
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;
Але що робити, якщо ви хочете створити безпечний стручок для результату Reference Type, що повертається 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}";
}
Отже, ви можете помітити, що не потрібно "писати допоміжний метод для кожного методу, який ви хочете використовувати" .
Ці два типи стручків (для ValueTypeResult
s- і ReferenceTypeResult
с) є досить .
Ось код SafePod
. Це не контейнер. Натомість він створює безпечну для винятків делегатну обгортку і для ValueTypeResult
s і 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;
}
}
Ось таким чином можна використовувати оператора ??
зведення нуля в поєднанні з потужністю першокласних організацій громадянdelegate
.