Монада MayBe
Что такое монада MayBe можно узнать из видео:
http://www.techdays.ru/videos/4448.html
Довольно-таки полноценный код монады:
using System.Collections; using System.Collections.Generic; using System.Linq; namespace System.Monads { /// <summary> Для словарей </summary> public static class MaybeIDictionary { /// <summary> Выполняет действие с каждым элементом словаря, если словарь существует </summary> public static IDictionary<TKey, TValue> Do<TKey, TValue>(this IDictionary<TKey, TValue> source, Action<TKey, TValue> action) { if (source != null) { foreach (var element in source) { action(element.Key, element.Value); } } return source; } /// <summary> Достает значение по указанному ключу, если словарь существует и имеет такой ключ. В противном случае возвращает значение типа по умолчанию. </summary> public static TValue With<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key) { if (source != null) { TValue result; if (source.TryGetValue(key, out result)) return result; } return default(TValue); } /// <summary> /// Достает значение по указанному ключу, если словарь существует и имеет такой ключ. В противном случае возвращает заданное нами значение по умолчанию </summary> public static TValue Return<TKey, TValue>(this IDictionary<TKey, TValue> source, TKey key, TValue defaultValue) { if (source != null) { TValue result; if (source.TryGetValue(key, out result)) return result; } return defaultValue; } } /// <summary> Для перечислений - массивы, списки </summary> public static class MaybeIEnumerable { /// <summary> Выполняет указанное действие с каждым элементом списка, если список существует </summary> public static IEnumerable Do(this IEnumerable source, Action<object> action) { var enumerable = source as object[] ?? source.Cast<object>().ToArray(); if (source != null) foreach (var element in enumerable) action(element); return enumerable; } /// <summary> Выполняет указанное действие с каждым элементом списка, если список существует </summary> public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> action) { var enumerable = source as TSource[] ?? source.ToArray(); if (source != null) foreach (var element in enumerable) action(element); return enumerable; } /// <summary> Выполняет указанное действие с каждым элементом списка, если список существует </summary> public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource, int> action) { var enumerable = source as TSource[] ?? source.ToArray(); if (source != null) foreach (var element in enumerable.Select((s, i) => new {Source = s, Index = i})) action(element.Source, element.Index); return enumerable; } /// <summary> Проталкивает указанное поле, если список существует. Аналог Select() грубо говоря </summary> public static IEnumerable<TResult> With<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> action) { return source != null ? source.Select(action) : null; } } public static class MaybeNullable { /// <summary> Выполняет указанное действие, если объект существует. </summary> public static TSource? Do<TSource>(this TSource? source, Action<TSource?> action) where TSource : struct { if (source.HasValue) action(source); return source; } /// <summary> Проталкивает указанное значение, если объект существует и значение типа по умолчанию, если не существует </summary> public static TResult With<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action) where TSource : struct { return source.HasValue ? action(source) : default(TResult); } /// <summary> Проталкивает указанное значение, если объект существует и значение указанного нами типа, если не существует </summary> public static TResult Return<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action, TResult defaultValue) where TSource : struct { return source.HasValue ? action(source) : defaultValue; } /// <summary> Проталкивает значение дальше, если выполняется условие, в противном случае возвращает значение типа по умолчанию </summary> public static TSource? If<TSource>(this TSource? source, Func<TSource?, bool> condition) where TSource : struct { return source.HasValue && condition(source) ? source : default(TSource); } /// <summary> Пироталкивает значение дальше, если условие не выполняется, в противном случае возвращает значение по умолчанию </summary> public static TSource? IfNot<TSource>(this TSource? source, Func<TSource?, bool> condition) where TSource : struct { return source.HasValue && !condition(source) ? source : default(TSource); } /// <summary> Если объект не существует, восстанавливает его указанным способом </summary> public static TInput Recover<TInput>(this TInput? source, Func<TInput> recoverFunc) where TInput : struct { return source.HasValue ? source.Value : recoverFunc(); } /// <summary> Выполняет метод если объект не пуст. Возвращает ошибку, если таковая произошла во время выполнения действия </summary> public static Tuple<TSource?, Exception> TryDo<TSource>(this TSource? source, Action<TSource?> action) where TSource : struct { if (source.HasValue) try { action(source); } catch (Exception ex) { return new Tuple<TSource?, Exception>(source, ex); } return new Tuple<TSource?, Exception>(source, null); } /// <summary> Выполняет метод если объект не пуст. Возвращает ошибку, если таковая произошла во время выполнения действия и только если она удовлетворяет условию </summary> public static Tuple<TSource?, Exception> TryDo<TSource>(this TSource? source, Action<TSource?> action, Func<Exception, bool> exceptionChecker) where TSource : struct { if (source.HasValue) try { action(source); } catch (Exception ex) { if (exceptionChecker(ex)) return new Tuple<TSource?, Exception>(source, ex); throw; } return new Tuple<TSource?, Exception>(source, null); } /// <summary> Выполняет метод если объект не пуст. Возвращает ошибку, если таковая произошла во время выполнения действия и только если она заранее указана в массиве </summary> public static Tuple<TSource?, Exception> TryDo<TSource>(this TSource? source, Action<TSource?> action, params Type[] expectedException) where TSource : struct { if (source.HasValue) try { action(source); } catch (Exception ex) { if (expectedException.Any(exp => exp.IsInstanceOfType(ex))) return new Tuple<TSource?, Exception>(source, ex); throw; } return new Tuple<TSource?, Exception>(source, null); } /// <summary> /// Allows to do some conversion of <paramref name="source"/> if its not null and catch any exceptions /// </summary> public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action) where TSource : struct { if (source.HasValue) { TResult result = default(TResult); try { result = action(source); return new Tuple<TResult, Exception>(result, null); } catch (Exception ex) { return new Tuple<TResult, Exception>(result, ex); } } return new Tuple<TResult, Exception>(default(TResult), null); } /// <summary> /// Allows to do some conversion of <paramref name="source"/> if its not null and catch exceptions, which handled by <param name="exceptionChecker"/> /// </summary> public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action, Func<Exception, bool> exceptionChecker) where TSource : struct { if (source.HasValue) { TResult result = default(TResult); try { result = action(source); return new Tuple<TResult, Exception>(result, null); } catch (Exception ex) { if (exceptionChecker(ex)) return new Tuple<TResult, Exception>(result, ex); throw; } } return new Tuple<TResult, Exception>(default(TResult), null); } /// <summary> /// Allows to do some conversion of <paramref name="source"/> if its not null and catch <param name="expectedException"/> exceptions /// </summary> public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource? source, Func<TSource?, TResult> action, params Type[] expectedException) where TSource : struct { if (source.HasValue) { TResult result = default(TResult); try { result = action(source); return new Tuple<TResult, Exception>(result, null); } catch (Exception ex) { if (expectedException.Any(exp => exp.IsInstanceOfType(ex))) return new Tuple<TResult, Exception>(result, ex); throw; } } return new Tuple<TResult, Exception>(default(TResult), null); } /// <summary> Возвращает true если объект не существует </summary> public static bool IsNull<TSource>(this TSource? source) where TSource : struct { return source.HasValue == false; } /// <summary> Возвращает true если объект существует </summary> public static bool IsNotNull<TSource>(this TSource? source) where TSource : struct { return source.HasValue; } } public static class MaybeObjects { /// <summary> Выполняет действие, если объект существует </summary> public static TSource Do<TSource>(this TSource source, Action<TSource> action) where TSource : class { if (source != default(TSource)) action(source); return source; } /// <summary> Проталкивает значение, если объект существует </summary> public static TResult With<TSource, TResult>(this TSource source, Func<TSource, TResult> action) where TSource : class { return source != default(TSource) ? action(source) : default(TResult); } /// <summary> Проталкивает значение, если объект существует, иначе возвращает наше значение по умолчанию </summary> public static TResult Return<TSource, TResult>(this TSource source, Func<TSource, TResult> action, TResult defaultValue) where TSource : class { return source != default(TSource) ? action(source) : defaultValue; } /// <summary> Проталкивает значение дальше, если выполняется условие </summary> public static TSource If<TSource>(this TSource source, Func<TSource, bool> condition) where TSource : class { return source != default(TSource) && condition(source) ? source : default(TSource); } /// <summary> Проталкивает значение дальше, если условие не выполняется</summary> public static TSource IfNot<TSource>(this TSource source, Func<TSource, bool> condition) where TSource : class { return source != default(TSource) && !condition(source) ? source : default(TSource); } /// <summary> Восстанавливает объект указанным способом если он не существует </summary> public static TInput Recover<TInput>(this TInput source, Func<TInput> action) where TInput : class { return source ?? action(); } /// <summary> Преобразует объект в указанный тип </summary> public static TResult OfType<TResult>(this object source) { return source is TResult ? (TResult) source : default(TResult); } /// <summary> /// Allows to do <paramref name="action"/> and catch any exceptions /// </summary> public static Tuple<TSource, Exception> TryDo<TSource>(this TSource source, Action<TSource> action) where TSource : class { if (source != default(TSource)) try { action(source); } catch (Exception ex) { return new Tuple<TSource, Exception>(source, ex); } return new Tuple<TSource, Exception>(source, null); } /// <summary> /// Allows to do <paramref name="action"/> and catch exceptions, which handled by <param name="exceptionChecker"/> /// </summary> public static Tuple<TSource, Exception> TryDo<TSource>(this TSource source, Action<TSource> action, Func<Exception, bool> exceptionChecker) where TSource : class { if (source != default(TSource)) try { action(source); } catch (Exception ex) { if (exceptionChecker(ex)) return new Tuple<TSource, Exception>(source, ex); throw; } return new Tuple<TSource, Exception>(source, null); } /// <summary> /// Allows to do <paramref name="action"/> and catch <param name="expectedException"/> exceptions /// </summary> public static Tuple<TSource, Exception> TryDo<TSource>(this TSource source, Action<TSource> action, params Type[] expectedException) where TSource : class { if (source != default(TSource)) try { action(source); } catch (Exception ex) { if (expectedException.Any(exp => exp.IsInstanceOfType(ex))) return new Tuple<TSource, Exception>(source, ex); throw; } return new Tuple<TSource, Exception>(source, null); } /// <summary> /// Allows to do some conversion of <paramref name="source"/> if its not null and catch any exceptions /// </summary> public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource source, Func<TSource, TResult> action) where TSource : class { if (source != default(TSource)) { TResult result = default(TResult); try { result = action(source); return new Tuple<TResult, Exception>(result, null); } catch (Exception ex) { return new Tuple<TResult, Exception>(result, ex); } } return new Tuple<TResult, Exception>(default(TResult), null); } /// <summary> /// Allows to do some conversion of <paramref name="source"/> if its not null and catch exceptions, which handled by <param name="exceptionChecker"/> /// </summary> public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource source, Func<TSource, TResult> action, Func<Exception, bool> exceptionChecker) where TSource : class { if (source != default(TSource)) { TResult result = default(TResult); try { result = action(source); return new Tuple<TResult, Exception>(result, null); } catch (Exception ex) { if (exceptionChecker(ex)) return new Tuple<TResult, Exception>(result, ex); throw; } } return new Tuple<TResult, Exception>(default(TResult), null); } /// <summary> /// Allows to do some conversion of <paramref name="source"/> if its not null and catch <param name="expectedException"/> exceptions /// </summary> public static Tuple<TResult, Exception> TryWith<TSource, TResult>(this TSource source, Func<TSource, TResult> action, params Type[] expectedException) where TSource : class { if (source != default(TSource)) { TResult result = default(TResult); try { result = action(source); return new Tuple<TResult, Exception>(result, null); } catch (Exception ex) { if (expectedException.Any(exp => exp.IsInstanceOfType(ex))) return new Tuple<TResult, Exception>(result, ex); throw; } } return new Tuple<TResult, Exception>(default(TResult), null); } /// <summary> /// Handle exception with no actions /// </summary> public static TSource Catch<TSource>(this Tuple<TSource, Exception> source) { return source.Item1; } /// <summary> /// Handle exception with <param name="handler"/> action /// </summary> public static TSource Catch<TSource>(this Tuple<TSource, Exception> source, Action<Exception> handler) { if (source.Item2 != null) handler(source.Item2); return source.Item1; } /// <summary> Возвращает true если объект не существует </summary> public static bool IsNull<TSource>(this TSource source) where TSource : class { return source == default(TSource); } /// <summary> Возвращает true если объект существует </summary> public static bool IsNotNull<TSource>(this TSource source) where TSource : class { return source != default(TSource); } } }
Исходник взят отсюда, от себя только частично комменты на русском + компоновка в один файлик + пара фиксов кода.
Монада очень важная и очень часто используемая, потому не запостить - грех.
Смотрите также:
Комментарии
Кстати для дебага строк на нуль можно добавить вот такой метод:
public static TInput Throw<TInput>(this TInput source, params string[] nullExceptionTexts) where TInput : class { if (source == null) throw new NullReferenceException(nullExceptionTexts != null ? string.Join(Environment.NewLine, nullExceptionTexts) : string.Format("Source of type {0} can not be null", typeof (TInput))); return source; }
Соответственно юзать например так
return s_parentType.Throw("SerializeMethod.s_parentType") .value.Throw("SerializeMethod.s_parentType.value");
То есть throw фактически выдаст исключение если появится нуль, при том не нужно разбивать всё на несколько строк + можно закинуть кастомные данные для фаст дебага.
Т.е. юнит тесты, содержащие некоторые проверки нужны, чтобы контроллировать, не забыл ли кто другой из команды программистов реализовать эти проверки в коде?
CollectableItemData.cs
[CreateMenuItem(fileName = "newItem", menuName = "Data/Items/Collectable", order = 51]