#c_sharp
Вопрос не про то, как красиво программировать, чтобы этот вопрос не возникал, а про красивую обертку над try-catch. try { } catch { // здесь ничего нет } эта хорошая штука, но но занимает много места, хотелось бы, чтобы умещалось в один блок, но выполняло тот же функционал Делаю кусок кода, который подстраховочный и вообще не достоин, чтобы на него уделяли большое внимание (сейчас уделяю такое внимание только на будущее, потому, что уже не раз задумывался над этой issue, ну и чтоб отдохнуть). Он не стоит того, чтобы делать рефакторинг, чтоб изящничать. Нужно просто и быстро поставить try, но хотелось бы чтобы это выглядело красиво. Если там что-то не сработает, то ничего страшного, но хотелось бы еще, чтобы это не повлекло за собой появление Exception-а уже в важных местах, поэтому вопрос про try-catch. Есть вариант сделать метод: private void Try(Action codeBlock) { try { codeBlock?.Invoke(); } catch { } } тогда вызов будет в одну строчку, но этот вариант не на 100% нравится: кажется не красивым вызов Try( ()=>DoWork() );, хотелось бы что-то попроще, без наворотов можно в аргумент передать вызов метода напрямую Try(MethodCall), это уже выглядит получше, но тогда в моем конкретном случае придется разбивать этот подстраховочный и второстепенный по важности метод на многие части, например, если случай такой: private static void EnsureSomethingWhichFailsAnyway(Someting input) { try { foreach (var x in input.GetAllX()) { DoSmallThing(x); try { x.SetPropertyValue = PossibleValues.BigValue; } catch { } } } catch { } try { foreach (var y in input.GetllY()) { try { y.Validate(StaticVars.A, StaticVars.B, StaticVars.C); } catch { } } } catch { } } то есть, если разбивать такой метод на подметоды для вызова типа Try(MethodCall) то игра не будет стоить свеч. можно ли как-то поместить в using? может быть нет идеального решения, тогда интересно просто услышать конструктивные мысли. Спасибо!
Ответы
Ответ 1
Клюнула идейка, которая позволяет уместиться в один блок. Использовать можно так: Try.AutoRunAction = () => { //code block }; Вот сырая реализация: public class Try { // runs on set public static Action AutoRunAction { set { try { value?.Invoke(); } catch { } } } } то же самое, что из метода, но меньше скобок раньше был геттер, который посоветовали убрать можно еще назвать AutoTryAction принимаю критику Пример: private static void SetAttributesNormal(DirectoryInfo dir) { AutoTryAction = () => { foreach (var subDir in dir.GetDirectories()) { SetAttributesNormal(subDir); AutoTryAction = () => subDir.Attributes = FileAttributes.Normal; } }; AutoTryAction = () => { foreach (var file in dir.GetFiles()) { AutoTryAction = () => file.Attributes = FileAttributes.Normal; } }; } private static Action AutoTryAction { set { try { value?.Invoke(); } catch { } } }Ответ 2
Я предлагаю наоборот обьеденить try-catch вместе, т.к. построение фрейма для ловли ошибок считаю затратной операцией. Сделать это можно через цикл. Пусть у вас 10 случаев. Вместо try{}catch{} прийдется писать case x:; break; боюсь сомнительный выигрыш. Разве что... авто-редактор кода не будет превращать это в 5 строк. int nmax = 10; int step = 0; while (step < nmax) try { // общий try for (int istep = step; istep < nmax; istep++) switch (istep) { case 0:; break; case 1:; break; // ..... } } catch { step++;} Обвертка вроде небольшая. Считаю что повысит быстродействие если не будет исключений... но незначительно. Более компактный вариант for (int step=0;step<10;step++) try { switch (step) { case 0:; break; //.... } } catch {}; Уже как я понял смысла практически не имеет. Вариант с Try(MethodCall) - чуть более затратный по времени. Вариант с using - не получится реализовать... явно, ну развечто если реализовать его так-само как и предыдущий вариант using (var x=new Try(MethodCall)) - что будет изврат. Что бы не было изврата... теоретически можно раскопать il-позицию... но на практике сделать проброс врядли выйдет. Вариант 2. Базируясь на варианте 1, и зная номер линии кода, можно шаманить. Но опять же, "условно". Если считать, что каждая строка выполняется один раз, то можно сделать так: bool ready = false; int step= 0; while (!ready) try { if (trysafe(ref step)) { method1; /*шаг 1*/ }; if (trysafe(ref step)) { method2; /*шаг 2*/}; ready = true; } catch { }; } // отсекатель bool trysafe(int ref kkey, [CallerLineNumber] int line = -1){ if (kkey < line ) return false; kkey = line; return true; } При исключении, произойдет цикл, и методы которые прошли в if будут пропущены независимо от того было исключение или нет. Отсекатель можно сделать через Dictionary (будет более "умный"). Но код должен предполагать разбивку на шаги как и в предыдущем случае, но шаги можно более "вольно" располагать. Промежутков между шагами не должно быть. Вызов trysafe - должен всегда происходить в разных строках программы. Аргумент kkey можно тоже сократить, и вынести в глобальную область, в зависимости от ситуации. P.S. Тихие исключения опасно использовать, потому что в итоге очень сложно отыскать ошибку.Ответ 3
Можно попробовать функционального подход: public readonly struct Try{ private readonly Lazy<(T, Exception)> factory; public Try(Func<(T, Exception)> factory) => this.factory = new Lazy<(T, Exception)>(() => { try { return factory(); } catch (Exception exception) { return (default, exception); } }); } public static class TryExtensions { public static Try SelectMany ( this Try source, Func > selector, Func resultSelector) => new Try (() => { if (source.HasException) { return (default, source.Exception); } Try result = selector(source.Value); if (result.HasException) { return (default, result.Exception); } return (resultSelector(source.Value, result.Value), (Exception)null); }); public static Try Try (this TSource value) => value; public static Try Select ( this Try source, Func selector) => source.SelectMany(value => selector(value).Try(), (value, result) => result); public static Try Throw ( this Exception exception) => new Try (() => (default, exception)); public static Try Try (Func function) => new Try (() => (function(), (Exception)null)); public static Try Catch ( this Try source, Func > handler, Func when = null) where TException : Exception => new Try (() => { if (source.HasException && source.Exception is TException exception && exception != null && ( when == null || when(exception))) { source = handler(exception); } return source.HasException ? (default, source.Exception) : (source.Value, (Exception)null); }); public static Try Catch ( this Try source, Func > handler, Func when = null) => Catch (source, handler, when); public static TResult Finally ( this Try source, Func , TResult> action) => action(source); public static void Finally ( this Try source, Action > action) => action(source); } Пример: internal static Try Example(int? value) { if (value == null) { return Throw (new ArgumentNullException(nameof(value))); } } Немного про то откуда это: Category Theory via C# Fundamentals Видео: uDev Tech Meetup #10: Функциональный C#
Комментариев нет:
Отправить комментарий