Добавлен , опубликован
Что такое монада MayBe можно узнать из видео:
Довольно-таки полноценный код монады:
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);
        }
    }
}
Исходник взят отсюда, от себя только частично комменты на русском + компоновка в один файлик + пара фиксов кода.
Монада очень важная и очень часто используемая, потому не запостить - грех.
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
20
10 лет назад
Отредактирован Mihahail
0
Я видимо сильно отстал от жизни, раз в таких языках как сишарп уже юзают монады.
Код не читал, только саммари, есть какие-нибудь отличия от хаскельных монад?
Я так понял, тут в основном реализован функционал итераторов для перечислимых объектов, с выбросом Nothing в случае ошибок?
Видео не смог посмотреть. Не ставить же сильверлайт?)
0
27
10 лет назад
0
Mihahail,
Шарп поддерживает монадические конструкции, хотя конечно не в той мере в которой это происходит в языках функционального программирования.
Соль не в перечисляемых объектах, если почитаешь монаду чуть пониже, то для обычных объектов она тоже юзается. В общем соль такова - это просто пробрасывание значения. Исключение постоянных проверок на Null.
Вот пример кода который я пишу прямо сейчас. Его смысл в том чтобы проверить, содержит ли текущий объект нужную мне конструкцию. В обычном коде он выглядит так:
bool result = false;
if (calc != null && calc.handle != null)
{
    var methodHandle = calc.handle as HandleMethod;
    if (methodHandle != null && methodHandle.method == checkMethod)
        result = true;
}
То есть фактически мне нужно просто пройти в calc.handle.method и сравнить его с моим checkMethod. При том если я не могу туда попасть по причине того что объектов нет - вернуть false
С помощью монады этот же самый код я могу забить однострочником
var result = calc.With(x => x.handle).OfType<HandleMethod>().With(x => x.method).If(x => x == checkMethod).ReturnSuccess();
Соответственно такая цепочка будет толкать null дальше, либо выполнять выражение.
С хаскеллом не работал, потому перечислить отличия не могу. Но это явно сдернуто оттуда.
0
29
10 лет назад
0
Фу однострочник....
А если calc будет наллом?
0
27
10 лет назад
0
А если calc будет наллом?
то все сработает точно так же
0
29
10 лет назад
0
Extravert, как то громоздко получается из-за вот этого OfType или у тебя handle не этого типа?
0
27
10 лет назад
0
Хочу заметить, я никого не принуждаю к использованию подобного метода. Но им пользуется огромное количество людей, в том числе и я сам. Раз я временами выкладываю наработки то было бы разумно опубликовать и такие куски, чтобы в дальнейшем свободно делать публикации с использованием этой монады.
Имхо монада дает неоспоримые плюсы. Например тот же пример что выше, немного перефразируем. Скажем у нас есть вот такая ситуация:
var result = calc != null && calc.handle != null && calc.handle.method != null && calc.handle.method.value != null ? calc.handle.method.value : null;
С монадой будет проще некуда:
var result = calc.With(x => x.handle).With(x => x.method).With(x => x.value);
Монада выглядит читабельней имхо.
Опять же, можно взять к примеру стандартные монады - Select, Where, Any - ведь они действительно удобны. Но всегда будут люди, которые скажут "фээ, я лучше буду разворачивать форичи вместо однострочника"
в первом примере handle у меня абстрактного типа Handle, и я привожу его к конкретному HandleMethod и только в случае если он имеет данный тип цепочка выполняется дальше.
0
29
10 лет назад
0
Extravert, просто я немного по другому оформляю
var result = calc
    .With(x => x.handle)
    .OrType<HandleMethod>()
    .With(x => x.method)
    .If(x => x == checkMethod)
    .ReturnSuccess();
Особенно касается всяких селектов и прочего. Правда не всегда LINQ выглядит красивее обычного foreach
в первом примере handle у меня абстрактного типа Handle, и я привожу его к конкретному HandleMethod и только в случае если он имеет данный тип цепочка выполняется дальше.
А ну все, больше претензий нет)
0
27
10 лет назад
0
просто я немного по другому оформляю
Я сам так оформляю, просто по причине того что я написал "однострочник" и изобразил одной строкой :)
бтв забыл сказать - код с 'Truple' только для новых шарпиков, скажем шарп в юнити его не понимает
0
29
10 лет назад
0
Extravert, надо чтоли тогда про Unity написать, правда не особо представляю как его можно использовать при создании игры
0
20
10 лет назад
0
Спасибо.
После конструкций хаскеля однострочник выглядит стремно(очень очень очень) =) Хотя я пожалуй всё понял. Но сам бы так не стал писать, наверно.
Ещё я плохо знаю сишарп, когда читаю исхожу из аналогий с С++, который знаю чуть лучше.
Ещё я плохо знаком с шаблонами, как по этой конструкции определить, где возникло исключение? А то протолкнем налл на самый верх, а где он появился? Это ведь придётся лезть в код монады чтобы она, скажем, кидала алерт куда-нибудь?
В хаскелле, кстати, это всё делается иначе и гораздо проще, с использованием механизма классов типов.. Я (ну, это видно) не часто комментирую такие топики, но тут конструкция из моего любимого(но не самого часто используемого) языка была перенесена на оо-язык, что вообще-говоря интересно само по себе. Вряд ли это самая полезная ссылка, которую я могу привести, но вот
0
29
10 лет назад
0
Mihahail, ну я думаю в случае написания такого кода использует совсем другой подход к проектированию системы и написанию коду, так чтобы исключений не было вообще или сводилось к минимуму. Ну и там есть-что то типа исключений, в код особо не смотрел, но что-то похожее есть
0
27
10 лет назад
Отредактирован Devion
0
Mihahail, там монады есть для trycatch. То есть достаточно дописать к названию монады слово Try в начале (например TryWith) и она будет выбивать екзепшн, если таковой есть. Но опять же ты прав - с однострочниками в этом плане всего туговато
На самом деле сам часто поглядываю на хаскель, но никак не могу его освоить. Пробовал брать литературу в руки, но там сразу все начинается с терминов аля комбинаторы/унарные методы/анализаторы без малейших разъяснений, потому ничего не понимая в фундаменте мне не дается понимание этой парадигмы и на этом я закругляюсь. Плюс когда смотрю даже на элементарный код - вопросов уйма, а спросить про них не у кого - хаскель не очень популярный язык чтобы было к кому обратиться, если есть вопросы.
0
20
9 лет назад
0
Extravert, я в процессе освоения сейчас, с основами вроде разобрался. По выходным я могу на любой вопрос дать любой ответ =)
Следует сразу сказать - он и правда расширяет мозг, но ничего серьезного(типа игрового движка) на нём нельзя написать просто. Лаконично - да, но не просто. Точнее оно когда-нибудь станет "просто", но для этого нужно знатно поднатореть во всём этом. И да, самому придумывать архитектуру каждой большой программы, ибо паттернов - как бы нет. И понятия "идиоматичный код на хаскеле" - как бы тоже. На русском уж точно такого материала не сыщешь, а на английском можно как раз за некоторыми best practices поглядеть Real World Haskell.
Действительно качественной литературы(особенно на русском) - вообще почти нет, а все книги "для уже программистов" - начинаются просто(имхо) не с того. Я заметил, что людей повергает в ступор вопрос о том, как же собственно работает программа, в которой нет никаких выражений вида "сделай вот это".
Мне на начальном этапе очень помогли две главы этой, хм, книги-руководства: anton-k.github.io/ru-haskell-book/book/1.html#%D0%B7%D0%BD%D0%B0...
Тут особенно важно, что вводится некий "вычислитель", действующий особым образом в соответствии с кодом на хаскеле.
0
29
9 лет назад
0
Ещё я плохо знаком с шаблонами, как по этой конструкции определить, где возникло исключение? А то протолкнем налл на самый верх, а где он появился?
Кст на счет этого, тут неожиданно вспомнил, чтобы не было нежданчиков, можно использовать юнит тесты и все будет ОК. Ну это если юнит тесты написаны хорошо конечно же
0
20
9 лет назад
0
alexprey, да как тебе сказать... Не писал ничего крупного после той моей попытки сделать леталку в космосе. Поэтому с юнит тестами знаком сугубо википедийно.
0
29
9 лет назад
0
Mihahail, суть юнит тестов, чтобы описать правильное поведение объекта для большинства случаев (пограничных значений). Если поведение меняется, то юнит тест падает. Хорошим тоном считается писать юнит тесты для класса еще до имплементации самих методов.
0
20
9 лет назад
Отредактирован Mihahail
0
alexprey, после эрлангов с хаскелем это кажется диким - прописывать каждый случай, каждую ветку ast. Или там не всё так плохо?
У меня есть роскошь: возможность серьезно изучать и пытаться писать на языках типа лиспа, хаскеля, эрланга. Т.е. то, что считается маргинальщиной и т.н. не нужно. Это мало способствует развитию навыков software engineering'a, зато интересно.
0
29
9 лет назад
0
Mihahail, обычно прописывают лишь пограничный значения для параметров. Например, метод
GetMessages(int[] messageIds)
Тут 2 пограничных значения:
1 - messageIds = null -> тут должен быть NullArgumentException
2 - messageIds = new int[] {} -> тут должен вернуть пустой список
Это и описывается в тест кейсах
0
20
9 лет назад
0
alexprey, в смысле? Разве GetMessages в описанных случаях не делает так как написано сам по себе?)
Или программист сам не уверен, как работает GetMessages?
0
29
9 лет назад
0
Mihahail, в случае говнокода да :D Суть в том, что когда работает много людей в проекте, все должно работать с примерно одинаковой логикой, как я и описал выше. Конечно но же можно и засунуть туда кейс, что при null возвращает все сообщения, но это весьма не очевидно. Почему именно те два кейса такие, ну например мы юзаем там SqlDataProvider и некороший программист написал его так:
IList<MessageEntity> GetMessages(int[] messageIds) {
   var collection = new EntityCollection<MessageEntity>();
   using(var cmd = new MySqlCommand()) {
       cmd.CommandText = string.Format(@"
SELECT *
FROM messages
WHERE MessageId IN ({0})
        ", messageIds.Join(","));

        FillEntityCollection(cmd, collection);
    }
    return collection;
}
Если у нас параметр null -> NullPointerException словим
Ecли у нас пустой массив -> MySqlException из-за того, что будет выражение вида
WHERE MessageId IN ()
Уже не однократно матерился на работе из-за того, что другие не обрабатывают эти 2 кейса и программа сваливается при проверке. Хотя все ждут что метод вернет пустое множество.
0
27
9 лет назад
0
Кстати для дебага строк на нуль можно добавить вот такой метод:
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 фактически выдаст исключение если появится нуль, при том не нужно разбивать всё на несколько строк + можно закинуть кастомные данные для фаст дебага.
0
20
9 лет назад
Отредактирован Mihahail
0
Т.е. юнит тесты, содержащие некоторые проверки нужны, чтобы контроллировать, не забыл ли кто другой из команды программистов реализовать эти проверки в коде?
0
29
9 лет назад
0
Mihahail, да и плюсом, не сломал ли кто-то когда правил/фиксил багу
Чтобы оставить комментарий, пожалуйста, войдите на сайт.