Подтвердить что ты не робот

Лучше ли возвращать сложный объект или использовать параметры ссылки/выхода?

Я собираю метод, который должен оценивать входные данные и возвращать true, если все условия выполнены или ложны, если какой-либо тест терпит неудачу. Я также хотел бы иметь какое-то сообщение о статусе, доступное для вызывающего, если есть сбой.

Проекты, с которыми я столкнулся, включают возврат bool и использование параметра out (или ref) для сообщения, возврату экземпляра (специально разработанного) класса с атрибутами bool и строки или даже возврат указателя перечисления или конкретной ошибки. какой лучший способ получить всю информацию из метода? Кто-нибудь из этих "хороших"? Есть ли у кого-нибудь другие рекомендации?

4b9b3361

Ответ 1

Обычно я пытаюсь вернуть сложный объект и вернуться к использованию параметра out, когда это необходимо.

Но вы смотрите на методы TryParse в .NET-преобразованиях, и они следуют шаблону возврата параметра bool и out из преобразованного значения. Таким образом, я не считаю, что плохо иметь параметры - это действительно зависит от того, что вы пытаетесь сделать.

Ответ 2

Я предпочитаю прямое возвращение типа, так как некоторые языки .NET могут не поддерживать параметры ref и out.

Вот хорошее объяснение от MS относительно того, почему.

Ответ 3

Возвращаемые объекты более читабельны и требуют меньше кода. Не существует различий в производительности, за исключением ваших собственных, пока вы прыгаете через обручи "вне параметров".

Ответ 4

Я думаю, это зависит от вашего определения добра.

Для меня я бы предпочел вернуть bool с параметром out. Я думаю, что это более читаемо. В этом случае Seoncd (и в некоторых случаях лучше) - Enum. Как личный выбор, я, как правило, не люблю возвращать класс только для данных сообщений; он абстрагирует то, что я делаю только на одном уровне слишком далеко для своих вкусов.

Ответ 5

Этот тип вещи легко представлен Int32.Parse() и Int32.TryParse(). Чтобы вернуть статус, если он терпит неудачу, или значение, если оно не требует, чтобы вы могли возвращать два разных типа, следовательно, параметр out в TryParse(). Возвращение специализированного объекта (по моему мнению) просто загромождает ваше пространство имен ненужными типами. Конечно, вы всегда можете исключить исключение из своего сообщения внутри него. Лично я предпочитаю метод TryParse(), потому что вы можете проверить статус, не создавая исключение, которое довольно тяжелое.

Ответ 6

Я бы настоятельно рекомендовал вернуть объект (сложный или другой), потому что, несомненно, вы можете узнать в будущем, что вам нужно вернуть дополнительную информацию, и если вы использовали комбинацию простого типа возврата с исходной ссылкой или двумя, вы застряли в добавлении дополнительных параметров, которые будут лишней загромождать вашу сигнатуру вашего метода.

Если вы используете объект, вы можете легко изменить его по дороге, чтобы поддерживать дополнительную информацию, которая вам нужна.

Я бы держался подальше от перечислений, если он НЕ ОЧЕНЬ упрощен и вряд ли изменится в будущем. В конечном итоге у вас будет меньше головных болей, и все будет проще, если вы вернете объект. Аргумент о том, что он будет помешать пространству имен, является слабым, если вы возвращаете "объекты" своим собственным пространством имен, но и каждому из них.

(Обратите внимание, что если вы хотите использовать перечисление, просто поставьте его внутри возвращаемого объекта, это даст вам простоту перечислений в сочетании с гибкостью объекта, способного на все, что вам нужно.)

Ответ 7

EDIT: я добавляю сводку моей точки вверху, для более удобного чтения:

  • Если ваш метод возвращает несколько частей данных, которые логически являются частью одной и той же "вещи", то определенно составьте их в сложный объект независимо от того, возвращаете ли вы этот объект в качестве возвращаемого значения или вывода параметр.

  • Если вашему методу необходимо вернуть какой-то статус (успех/отказ/сколько записей обновлено /etc.) в дополнение к данным, тогда подумайте о возврате своих данных в качестве выходного параметра и использовании возвращаемого значения для возврата статус

Существует два варианта ситуаций, к которым применяется этот вопрос:

  • Требуется вернуть данные, состоящие из нескольких атрибутов

  • Требуется вернуть данные вместе со статусом действия, используемого для получения данных

Для № 1, я считаю, что если у вас есть данные, состоящие из нескольких атрибутов, которые все объединяются, то они должны быть сгруппированы в один тип как класс или структура, а один объект должен быть возвращен как возвращаемый значение метода или как выходной параметр.

Для # 2, я думаю, это тот случай, когда выходные параметры действительно имеют смысл. Концептуально методы обычно выполняют действие; в этом случае я предпочитаю использовать возвращаемое значение метода для указания статуса действия. Это может быть просто логическим, чтобы указать успех или неудачу, или это может быть нечто более сложное, например перечисление или строка, если существует несколько возможных состояний для описания действия, которое выполнял метод.

Если использовать выходные параметры, я бы посоветовал человеку использовать только один (см. пункт № 1), если только не существует конкретной причины использовать более одного. Не используйте несколько параметров вывода просто потому, что данные, которые вам нужно вернуть, состоят из нескольких атрибутов. Используйте только несколько выходных параметров, если это специально определяет семантика вашего метода. Ниже приведен пример, где я думаю, что несколько выходных параметров имеют смысл:

    // an acceptable use of multiple output parameters
    bool DetermineWinners(IEnumerable<Player> players, out Player first, out Player second, out Player third)
    {
        // ...
    }

И наоборот, вот пример, когда я думаю, что несколько выходных параметров не имеют смысла.

    // Baaaaad
    bool FindPerson(string firstName, string lastName, out int personId, out string address, out string phoneNumber)
    {
        // ...
    }

Атрибуты данных (personId, address и phoneNumber) должны быть частью объекта Person, возвращаемого этим методом. Лучшей версией этого было бы следующее:

    // better
    bool FindPerson(string firstName, string lastName, out Person person)
    {
        // ...
    }

Ответ 8

Я предпочитаю объект для парм в основном потому, что вы можете больше проверять допустимые значения в "Set" для свойств.

Это часто упускается из виду в веб-приложениях и базовой мерой безопасности.

Руководства OWASP указывают, что значения должны быть проверены (с использованием проверки белого списка) каждый раз, когда они назначены. Если вы собираетесь использовать эти значения в более чем одном месте, гораздо проще создать объект или структуру для хранения значений и проверить там, а не проверять каждый раз, когда у вас есть парам в коде.

Ответ 9

Это может быть субъективным.

Но, как всегда, я бы сказал, что это в значительной степени зависит и что нет никакого "правильного" способа сделать это. Например, шаблон TryGet(), используемый словарями, возвращает bool (который часто потребляется в if сразу) и эффективный тип возврата как out. Это имеет смысл в этом сценарии.

Однако, если вы перечисляете элементы, которые вы получаете KeyValuePair<,>, что также имеет смысл, поскольку вам может понадобиться как ключ, так и значение как один "пакет".

В вашем конкретном случае у меня может возникнуть искушение на самом деле ожидать результата bool и передать дополнительный (null разрешенный) ICollection<ErrorMessage> экземпляр, который получает ошибки (ErrorMessage может быть просто String а если это достаточно). Это позволяет сообщать о нескольких ошибках.

Ответ 10

Я использую перечисления, но использую их как шаблон флагов/бит. Таким образом, я могу указать несколько неудачных состояний.

[Flags]
enum FailState
{
    OK = 0x1; //I do this instead of 0 so that I can differentiate between
              // an enumeration that hasn't been set to anything 
              //and a true OK status.
    FailMode1 = 0x2;
    FailMode2 = 0x4;
}

Итак, тогда в моем методе я просто делаю это

FailState fail;

if (failCondition1)
{
    fail |= FailState.FailMode1;
}
if (failCondition2)
{
    fail |= FailState.FailMode2;
}

if (fail == 0x0)
{
    fail = FailState.OK;
}

return fail;

Досадная вещь об этом подходе заключается в том, что определение того, установлен ли бит в перечислении, выглядит следующим образом:

if (FailState.FailMode1 == (FailState.FailMode1 && fail))
{
    //we know that we failed with FailMode1;
}

Я использую методы расширения, чтобы дать мне метод IsFlagSet() для моих перечислений.

public static class EnumExtensions
{
    private static void CheckIsEnum<T>(bool withFlags)
    {
        if (!typeof(T).IsEnum)
            throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName));
        if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
            throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName));
    }

    public static bool IsFlagSet<T>(this T value, T flag) where T : struct
    {
        CheckIsEnum<T>(true);
        long lValue = Convert.ToInt64(value);
        long lFlag = Convert.ToInt64(flag);
        return (lValue & lFlag) != 0;
    }
}

Я скопировал этот код из другого ответа здесь, на SO, но я понятия не имею, где этот ответ на данный момент...

Это позволяет мне просто сказать

if (fail.IsFlagSet(FailState.Ok))
{
    //we're ok
}

Ответ 11

Метод TryParse (...) не лучший пример в этом случае. TryParse только сигнализирует, если число было проанализировано или нет, но в нем не указано, почему синтаксический анализ завершился неудачно. Это не нужно, потому что единственная причина, по которой он может потерпеть неудачу, - это "плохой формат".

Когда вы сталкиваетесь с ситуациями, когда нет очевидной хорошей сигнатуры метода для того, что вы пытаетесь сделать, вы должны отступить и спросить себя, может ли этот метод делать слишком много.

Какие тесты вы используете? Они вообще связаны? Существуют ли тесты в этом же методе, которые не срабатывали бы из-за совершенно разных несвязанных причин, которые вы должны отслеживать, чтобы дать значимую обратную связь пользователю/потребителю?

Будет ли рефакторинг вашей кодовой помощи? Можете ли вы логически группировать тесты на отдельные методы, которые могут быть сбой только по одной причине (принцип единой цели) и вызвать их все соответственно из вашего исходного сайта вместо одного многоцелевого метода, который вы рассматриваете?

Является ли неудачный тест нормальной ситуацией, которую нужно только регистрировать в каком-либо файле журнала, или обязательно, чтобы вы сообщали пользователю каким-то способом, возможно, остановив нормальный поток приложения? Если его последний, можно ли использовать пользовательские исключения и уловить их соответственно?

Есть много возможностей, но нам нужно больше информации о том, что вы пытаетесь сделать, чтобы дать вам более точные советы.

Ответ 12

Мы делаем что-то очень похожее на наши сервисы WCF с использованием объектов Request и Response, а исключения перекрываются полностью (не обрабатываются самим методом) и обрабатываются настраиваемым атрибутом (написанным в PostSharp), который ловит исключение и возвращает ответ "отказ", например:

[HandleException]
public Response DoSomething(Request request)
{
   ...

   return new Response { Success = true };
}

public class Response
{
   ...
   public bool Success { get; set; }
   public string StatusMessage { get; set; }
   public int? ErrorCode { get; set; }
}

public class Request
{
   ...
}

Логика обработки исключений в атрибуте может использовать сообщение об исключении для установки StatusMessage, и если у вас есть набор пользовательских исключений в вашем приложении, вы можете даже установить ErrorCode, где это возможно.

Ответ 13

Я думаю, что это зависит от уровня сложности и может ли операция потерпеть неудачу, в результате чего... что?

В случае примеров TryParse вы обычно не можете вернуть недопустимое значение (например, нет такой вещи, как "недопустимый" int) и определенно не может вернуть тип нулевого значения (соответствующий Parse методы не возвращаются Nullable<T>). Кроме того, для возврата Nullable<T> потребуется ввести переменную Nullable<T> на сайт вызова, условную проверку, чтобы увидеть, является ли это значение null, а затем приведение к типу T, которое должно дублировать время выполнения проверьте, имеет ли значение значение null, прежде чем вернуть это фактическое ненулевое значение. Это менее эффективно, передавая результат как параметр out и указывая через успешное или неудачное значение возвращаемого значения (очевидно, в этом случае вас не волнует, почему он не прошел, только то, что он сделал). В предыдущих версиях .Net единственным выбором были методы Parse, которые бросали какой-либо отказ. Исключение исключений .Net дорого, особенно относительно возврата флага true/false, указывающего статус.

Что касается вашего собственного дизайна интерфейса, вам нужно подумать, зачем вам нужен параметр out. Возможно, вам нужно вернуть несколько значений из функции и не использовать анонимные типы. Возможно, вам нужно вернуть тип значения, но не может получить подходящее значение по умолчанию в соответствии с входами.

Параметры

out и ref не являются no-nos, но ваш интерфейс должен быть тщательно рассмотрен, насколько они необходимы или нет, прежде чем вы их используете. Стандартный возврат по ссылке должен быть достаточным в более чем 99% случаев, но я бы посмотрел на использование параметров out или ref, прежде чем излишне определять новый тип, просто чтобы удовлетворить возврат "одного" объекта вместо нескольких значений.

Ответ 14

"Я собираю метод, который должен оценивать ввод и return true, если выполнены все условия или false, если какой-либо тест не прошел. Я бы тоже нравится иметь какой-то статус сообщение, доступное вызывающему, если там провал."

Если у вашего метода есть условия в отношении ввода, и если они не выполнены, вы можете очень сильно подумать о том, чтобы выбрасывать соответствующие исключения. Бросив правильные исключения, вызывающий узнает, что именно пошло не так. Это более удобный и масштабируемый подход. Возвращаемый код ошибки не является хорошим вариантом по понятным причинам.

"Проекты, с которыми я столкнулся, включают возврат bool и использование параметра out (или ref) для сообщения, возвращающего экземпляр класса (специально разработанного) с атрибутами bool и string или даже возвращающее перечисление, указывающее или конкретная ошибка."

  • Сложные объекты - это самый очевидный вариант. (класс или структура)

  • Параметр out: "Один из способов определения параметров - это то, что они похожи на дополнительные возвращаемые значения метода. очень удобны, когда метод возвращает более одного значения, в этом примере firstName и lastName. Однако параметры out можно злоупотреблять. В качестве хорошего стиля программирования, если вы обнаруживаете, что пишете метод с множеством параметров, вам следует подумать о рефакторинге ваш код. Одно из возможных решений - упаковать все возвращаемые значения в одну структуру". Слишком много параметров или ref указывает на дефект дизайна.

  • Tuple: "группируйте вместе нечеткие несвязанные данные в некоторой структуре, которая является более легкой, чем класс",

Должен читать: Подробнее о кортежах: