Что лучше, возвращаемое значение или выходной параметр?

StackOverflow https://stackoverflow.com/questions/810797

  •  03-07-2019
  •  | 
  •  

Вопрос

Если мы хотим получить значение из метода, мы можем использовать любое возвращаемое значение, например:

public int GetValue(); 

или:

public void GetValue(out int x);

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

Спасибо.

Это было полезно?

Решение

Возвращаемые значения почти равны всегда правильный выбор, когда методу больше нечего возвращать.(На самом деле, я не могу припомнить ни одного случая, когда я бы когда - либо хотите метод void с out параметр, если бы у меня был выбор.C# 7-е Deconstruct методы деконструкции с поддержкой языка являются очень, очень редким исключением из этого правила.)

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

int foo;
GetValue(out foo);

против

int foo = GetValue();

Значения Out также предотвращают связывание методов подобным образом:

Console.WriteLine(GetValue().ToString("g"));

(Действительно, это также одна из проблем с установщиками свойств, и именно поэтому шаблон builder использует методы, которые возвращают builder, например myStringBuilder.Append(xxx).Append(yyy).)

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

Возвращаемые значения FTW.

Редактировать:С точки зрения того, что происходит...

По сути, когда вы передаете аргумент для параметра "out", вы иметь для передачи в переменную.(Элементы массива также классифицируются как переменные.) Вызываемый вами метод не имеет "новой" переменной в своем стеке для параметра - он использует вашу переменную для хранения.Любые изменения в переменной видны сразу.Вот пример, показывающий разницу:

using System;

class Test
{
    static int value;

    static void ShowValue(string description)
    {
        Console.WriteLine(description + value);
    }

    static void Main()
    {
        Console.WriteLine("Return value test...");
        value = 5;
        value = ReturnValue();
        ShowValue("Value after ReturnValue(): ");

        value = 5;
        Console.WriteLine("Out parameter test...");
        OutParameter(out value);
        ShowValue("Value after OutParameter(): ");
    }

    static int ReturnValue()
    {
        ShowValue("ReturnValue (pre): ");
        int tmp = 10;
        ShowValue("ReturnValue (post): ");
        return tmp;
    }

    static void OutParameter(out int tmp)
    {
        ShowValue("OutParameter (pre): ");
        tmp = 10;
        ShowValue("OutParameter (post): ");
    }
}

Результаты:

Return value test...
ReturnValue (pre): 5
ReturnValue (post): 5
Value after ReturnValue(): 10
Out parameter test...
OutParameter (pre): 5
OutParameter (post): 10
Value after OutParameter(): 10

Разница заключается на этапе "после", т. е.после изменения локальной переменной или параметра.В тесте returnValue это не имеет никакого значения для статического value переменная.В тесте с внешним параметром value переменная изменяется с помощью строки tmp = 10;

Другие советы

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

public int ReturnMultiple(int input, out int output1, out int output2)
{
    output1 = input + 1;
    output2 = input + 2;

    return input;
}

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

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

Как правило, вы должны предпочесть возвращаемое значение параметру out.Параметры Out являются необходимым злом, если вы обнаруживаете, что пишете код, который должен делать 2 вещи.Хорошим примером этого является шаблон Try (такой как Int32.TryParse ).

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

int foo = GetValue();

Обратите внимание, что я могу объявить переменную и присвоить ее с помощью вашего метода в одной строке.Для 2-го примера это выглядит примерно так...

int foo;
GetValue(out foo);

Теперь я вынужден заранее объявить свою переменную и написать свой код в две строки.

Обновить

Хорошее место, на которое стоит обратить внимание, задавая вопросы такого типа, - это .Рекомендации по разработке NET Framework.Если у вас есть книжная версия, то вы можете ознакомиться с аннотациями Андерса Хейлсберга и других авторов по этому вопросу (стр. 184-185), но онлайн-версия находится здесь...

http://msdn.microsoft.com/en-us/library/ms182131 (ПРОТИВ 80).aspx

Если вам понадобится вернуть две вещи из API, то объединение их в структуру / класс было бы лучше, чем параметр out.

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

 Method1();  // Return values can be discard quite easily, even accidentally

 int  resultCode;
 Method2(out resultCode);  // Out params are a little harder to ignore

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

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

В основном это предпочтения

Я предпочитаю возвраты, и если у вас есть несколько возвратов, вы можете обернуть их в Result DTO

public class Result{
  public Person Person {get;set;}
  public int Sum {get;set;}
}

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

Наиболее примечательное исключение, которое приходит на ум, - это когда вы хотите вернуть несколько значений (в .Net Framework нет кортежей до версии 4.0), например, с TryParse закономерность.

У вас может быть только одно возвращаемое значение, в то время как у вас может быть несколько выходных параметров.

Вам нужно учитывать эти параметры только в этих случаях.

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

Я бы предпочел следующее вместо любого из приведенных в этом простом примере.

public int Value
{
    get;
    private set;
}

Но все они очень похожи.Обычно используется 'out' только в том случае, если им нужно передать несколько значений обратно из метода.Если вы хотите отправлять значение в метод и из него, следует выбрать "ref".Мой метод лучше всего подходит, если вы возвращаете только значение, но если вы хотите передать параметр и получить значение обратно, вы, скорее всего, выберете свой первый вариант.

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

Если вы используете return, то данные сначала записываются в стек методов, а затем в стек вызывающего метода.В то время как в случае out он записывается непосредственно в стек вызывающих методов.Не уверен, есть ли еще какие-то различия.

Реальной разницы нет, наши параметры находятся в C #, чтобы позволить методу возвращать более одного значения, вот и все.

Однако есть некоторые незначительные различия , но ни одно из них не является по - настоящему важным:

Использование параметра out принудит вас использовать две строки типа:

int n;
GetValue(n);

при использовании возвращаемого значения это позволит вам сделать это в одной строке:

int n = GetValue();

Другое отличие (правильное только для типов значений и только в том случае, если C # не встроит функцию) заключается в том, что использование возвращаемого значения обязательно создаст копию значения при возврате функции, в то время как использование параметра OUT не обязательно сделает это.

Как говорили другие:возвращаемое значение, а не параметр out.

Могу ли я порекомендовать вам книгу "Рекомендации по разработке фреймворков" (2-е изд.)?На страницах 184-185 описаны причины, по которым следует избегать параметров out.Вся книга направит вас в правильном направлении по всем видам проблем, связанных с .NET-кодированием.

В соответствии с Рекомендациями по проектированию фреймворка рекомендуется использовать инструмент статического анализа FxCop.Вы найдете это на сайтах Microsoft в качестве бесплатной загрузки.Запустите это в вашем скомпилированном коде и посмотрите, что он скажет.Если он жалуется на сотни и сотни вещей...не паникуйте!Посмотрите спокойно и внимательно на то, что там говорится о каждом конкретном случае.Не спешите исправлять ситуацию как можно скорее.Извлекайте уроки из того, что он вам говорит.Вы встанете на путь к мастерству.

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

Кроме того, возвращаемые значения совместимы с парадигмами асинхронного проектирования.

Вы не можете назначить функцию "асинхронной", если она использует параметры ref или out.

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

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

var result = DoThing();
if (result.Success)
{
    result = DoOtherThing()
    if (result.Success)
    {
        result = DoFinalThing()
        if (result.Success)
        {
            success = true;
        }
    }
}

против:

var result;
if (DoThing(out result))
{
    if (DoOtherThing(out result))
    {
        if (DoFinalThing(out result))
        {
            success = true;
        }
    }
}

out более полезен, когда вы пытаетесь вернуть объект, который вы объявляете в методе.

Пример

public BookList Find(string key)
{
   BookList book; //BookList is a model class
   _books.TryGetValue(key, out book) //_books is a concurrent dictionary
                                     //TryGetValue gets an item with matching key and returns it into book.
   return book;
}

возвращаемое значение это обычное значение, которое возвращается вашим методом.

Где как вон parameter, well out и ref - это 2 ключевых слова C #, которые позволяют передавать переменные как ссылка.

Большая разница между ссылка и вон является, ссылка должен быть инициализирован до и вон не надо

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

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

"VRP" - это современное академическое название функции, которая вызывается как часть выражения и имеет возвращаемое значение, которое условно заменяет вызов во время вычисления выражения.Например.в таком заявлении , как x = 1 + f(y) функция f служит в качестве VRP.

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

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

Таким образом, если в C # ваша функция не является детерминированной и чистой, я говорю, что вы должны сделать ее void функция (другими словами, не VRP), и любое значение, которое она должна вернуть, должно быть возвращено либо в out или ref параметр.

Например, если у вас есть функция для удаления некоторых строк из таблицы базы данных, и вы хотите, чтобы она возвращала количество удаленных строк, вы должны объявить ее примерно так:

public void DeleteBasketItems(BasketItemCategory category, out int count);

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top