Что лучше, возвращаемое значение или параметр out?

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

public int GetValue(); 

или

public void GetValue(out int x);

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

Спасибо.

Ответ 1

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

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

int foo;
GetValue(out foo);

против

int foo = GetValue();
Значения

Out также предотвращают цепочку методов следующим образом:

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

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

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

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

EDIT: Что касается того, что происходит...

В основном, когда вы передаете аргумент для параметра "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. В тесте OutParameter переменная value изменяется по строке tmp = 10;

Ответ 2

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

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

int foo = GetValue();

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

int foo;
GetValue(out foo);

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

Обновление

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

http://msdn.microsoft.com/en-us/library/ms182131(VS.80).aspx

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

Ответ 3

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

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

    return input;
}

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

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

Ответ 4

Есть одна причина использовать параметр 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, но вы обратили на это внимание.

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

Ответ 5

Это предпочтение в основном

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

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

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

public int Value
{
    get;
    private set;
}

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

Ответ 9

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

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

Ответ 10

Нет никакой реальной разницы, параметры в С#, чтобы позволить методу возвращать более одного значения, вот и все.

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

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

int n;
GetValue(n);

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

int n = GetValue();

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

Ответ 11

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

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

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

Ответ 12

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

Ответ 13

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

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

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

Ответ 14

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;
}

Ответ 15

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

Где в качестве параметра out, наилучшим образом и ref являются 2 ключевыми словами С#, они позволяют передавать переменные как ссылка.

Большая разница между ref и out: ref должна быть инициализирована до и вне.

Ответ 16

Используя ключевое слово 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;
        }
    }
}