Кажется, что целью ОП было найти хороший образец для решения своей проблемы и решения текущей проблемы, с которой он боролся в тот момент.
ОП: «Я мог бы обернуть каждый расчетв вспомогательном методе, который возвращает null при ошибке, а затем просто использует оператор ??
, но есть ли способ сделать это в более общем смысле (т.е. без необходимости писать вспомогательный метод для каждого метода, который я хочу использовать)? I 'Мы думали о написании статического метода с использованием обобщений, который оборачивает любой данный метод в try / catch и возвращает null при ошибке, но я не уверен, как бы я поступил об этом. Любые идеи? "
Я видел много хороших шаблонов, которые избегают вложенных блоков try catch , опубликованных в этом фиде, но не нашел решения указанной выше проблемы.Итак, вот решение:
Как уже упоминалось выше, OP хотел создать объект-оболочку , который возвращает null
при ошибке .Я бы назвал это pod ( Безопасный модуль ).
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;
Но что, если вы хотите создать безопасный модуль для результата типа ссылки , возвращаемого функциями / методами 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
с и 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
с).