это - лучшая практика для возврата сложного объекта или ссылочных / параметров использования?

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

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

17
задан lincolnk 28 July 2010 в 19:59
поделиться

14 ответов

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

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

13
ответ дан 30 November 2019 в 12:26
поделиться

Мы делаем что-то очень похожее с нашими службами 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 там, где это возможно.

0
ответ дан 30 November 2019 в 12:26
поделиться

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

2
ответ дан 30 November 2019 в 12:26
поделиться

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

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

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

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

0
ответ дан 30 November 2019 в 12:26
поделиться

Я предпочитаю объект, а не список парм, в основном потому, что вы можно выполнить дополнительную проверку допустимых значений в «Наборе» свойств.

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

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

1
ответ дан 30 November 2019 в 12:26
поделиться

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

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

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

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

2
ответ дан 30 November 2019 в 12:26
поделиться

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

[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
}
1
ответ дан 30 November 2019 в 12:26
поделиться

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

3
ответ дан 30 November 2019 в 12:26
поделиться

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

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

2
ответ дан 30 November 2019 в 12:26
поделиться

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

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

4
ответ дан 30 November 2019 в 12:26
поделиться

EDIT: Я добавляю краткое изложение моей точки зрения вверху, чтобы было легче читать:

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

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

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

  1. Нужно вернуть данные, состоящие из нескольких атрибутов

  2. Нужно вернуть данные вместе со статусом действия, использованного для получения этих данных

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

Для #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)
    {
        // ...
    }
2
ответ дан 30 November 2019 в 12:26
поделиться

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

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

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

  1. Сложные объекты, конечно, являются наиболее очевидным вариантом. (класс или структура)

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

  3. Tuple: "группировать кучу несвязанных между собой данных в некоторую структуру, более легковесную, чем класс"

Обязательно к прочтению: Больше о кортежах:

0
ответ дан 30 November 2019 в 12:26
поделиться

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

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

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

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

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

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

0
ответ дан 30 November 2019 в 12:26
поделиться

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

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

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

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

1
ответ дан 30 November 2019 в 12:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: