Возвращая два значения, Кортеж vs ' out ' vs 'struct'

рассмотрим функцию, которая возвращает два значения. Мы можем написать:

// Using out:
string MyFunction(string input, out int count)

// Using Tuple class:
Tuple<string, int> MyFunction(string input)

// Using struct:
MyStruct MyFunction(string input)

какой из них является лучшей практикой и почему?

6 ответов


У каждого из них есть свои плюсы и минусы.

параметры Out быстры и дешевы, но требуют, чтобы вы передали переменную и полагались на мутацию. Практически невозможно правильно использовать параметр out с LINQ.

кортежи делают давление собрания и ООН-само-документировать. "Item1" не очень описательно.

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

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

UPDATE: обратите внимание, что кортежи в C# 7, которые были отправлены через шесть лет после написания этой статьи, являются типами значений и, следовательно, с меньшей вероятностью создают давление на сбор.


Я думаю, что ответ зависит от семантики того, что функция делает, и отношения между двумя значениями.

например,TryParse методы взять out параметр для принятия анализируемого значения и возврата bool чтобы указать, удалось ли выполнить синтаксический анализ. Эти два значения на самом деле не принадлежат друг другу, поэтому семантически это имеет больше смысла, и намерение кода легче читать, использовать


добавление к предыдущим ответам, C# 7 приносит кортежи типа значения, в отличие от System.Tuple это ссылочный тип, а также предлагает улучшенную семантику.

вы все равно можете оставить их без имени и использовать .Item* синтаксис:

(string, string, int) getPerson()
{
    return ("John", "Doe", 42);
}

var person = getPerson();
person.Item1; //John
person.Item2; //Doe
person.Item3;   //42

но что действительно мощно в этой новой функции, так это возможность именовать кортежи. Так что мы можем переписать, как это:

(string FirstName, string LastName, int Age) getPerson()
{
    return ("John", "Doe", 42);
}

var person = getPerson();
person.FirstName; //John
person.LastName; //Doe
person.Age;   //42

Destructuring также поддерживается:

(string firstName, string lastName, int age) = getPerson()


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


вы не упомянули еще один вариант, который имеет пользовательский класс вместо структуры. Если данные имеют семантику, связанную с ним, которая может управляться функциями, или размер экземпляра достаточно большой (> 16 байт, как правило), пользовательский класс может быть предпочтительным. Использование "out" не рекомендуется в общедоступном API из-за его связи с указателями и требует понимания того, как ссылочные типы работа.

https://msdn.microsoft.com/en-us/library/ms182131.aspx

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


нет "лучшей практики". Это то, что вам удобно и что лучше всего работает в вашей ситуации. Пока вы согласны с этим, нет никаких проблем с любым из решений, которые вы опубликовали.