2009-05-01 2 views
122

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

public int GetValue(); 

или:

public void GetValue(out int x); 

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

Спасибо.

+3

Я бы хотел, чтобы у C# было несколько возвращаемых значений, например Python. – Trap

+6

@Trap Вы можете вернуть «Tuple», если хотите, но общий консенсус в том, что если вам нужно вернуть несколько вещей, вещи обычно связаны каким-то образом, и это отношение обычно лучше всего выражается как класс. – Pharap

+1

@Pharap Tuples в C# в его текущей форме просто уродливые, но это только мое мнение. С другой стороны, «общий консенсус» ничего не значит с точки зрения удобства использования и производительности. Вы бы не создали класс для возврата нескольких значений по той же причине, что вы не создали бы класс, чтобы вернуть пару значений в качестве параметра ref/out. – Trap

ответ

131

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

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

int foo; 
GetValue(out foo); 

против

int foo = GetValue(); 

Out значения также предотвратить метод цепочки, как это:

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

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

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

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

EDIT: С точки зрения того, что происходит ...

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

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, отличаются ли они тем, как они создаются, назначаются и возвращаются? – Vimvq1987

+0

Да. Я отредактирую, чтобы показать пример разницы. –

+1

TryParse - лучший пример, когда использование параметра out является подходящим и чистым. Хотя я использовал его в особых случаях, например, если (WorkSucceeded (out List )), который в основном такой же, как TryParse –

2

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

public int Value 
{ 
    get; 
    private set; 
} 

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

7

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

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

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

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

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

    return input; 
} 

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

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

+2

Я не согласен с этим, почему бы вам не возвращать структуру данных с 4 Интса ? Это действительно сбивает с толку. –

+1

Очевидно, что существует больше (и лучше) способов возврата нескольких значений, я просто даю ОП причины, из-за которых существует в первую очередь. – pyrocumulus

+1

Я согласен с @Cloud. Просто потому, что это не лучший способ, это не значит, что он не должен существовать. – Cerebrus

5

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

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

+0

Не уверен, что его хорошая практика, но Arraylist также может использоваться для возврата нескольких значений. –

23

Обычно вы предпочитаете возвращаемое значение по параметру 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(VS.80).aspx

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

+0

Отличный ответ, особенно ссылка на TryParse, (общая) функция, которая заставляет разработчиков использовать (необычные) переменные. – Cerebrus

1

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

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

+0

Методы стека? Я не эксперт C#, но x86 поддерживает только один стек на поток. «Кадр» метода освобождается во время возврата, и если произойдет переход контекста, то освобожденный стек может быть перезаписан. В c все возвращаемые значения идут в реестре eax. Если вам нравится возвращать объекты/структуры, они должны быть выделены в кучу, и указатель будет помещен в eax. –

4

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

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

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

1

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

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

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

int n; 
GetValue(n); 

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

int n = GetValue(); 

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

+0

Как упоминалось выше, существует несколько отличий. – Skelly1983

1

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

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

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

1

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

9

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

0

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

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

Где, как из параметра, хорошо, и исх 2 ключевые слова C# они позволяют передавать переменные в качестве ссылки.

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

1

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

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

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

+0

Отличная точка в обозначении «асинхронное». Думал, что кто-то еще упомянул об этом. Цепочка - а также использование возвращаемого значения (собственно, самой функции) в качестве выражения является еще одним ключом. На самом деле разница между рассмотрением только того, как просто вернуть материал из процесса («ведро» - обсуждение Tuple/class/struct) и рассматривать сам процесс как выражение, которое может быть заменено на одно значение (полезность сама функция, потому что она возвращает только 1 значение). – user1172173

0

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

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; 
     } 
    } 
} 
Смежные вопросы