Возвращая два значения, Tuple 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)
Какая из них - лучшая практика и почему?
Ответы
Ответ 1
У каждого из них свои плюсы и минусы.
Параметры параметров бывают быстрыми и дешевыми, но требуют, чтобы вы проходили переменную и полагались на мутацию. Практически невозможно правильно использовать параметр out с LINQ.
Кортежи производят давление на сбор и не самодокументируются. "Item1" не очень описателен.
Пользовательские структуры могут медленно копироваться, если они большие, но самодокументированы и эффективны, если они небольшие. Однако также больно определить целую кучу пользовательских структур для тривиального использования.
Я был бы склонен к тому, чтобы пользовательское структурное решение при прочих равных условиях. Еще лучше, однако, сделать функцию, которая возвращает только одно значение. Почему вы в первую очередь возвращаете два значения?
UPDATE: обратите внимание, что кортежи в С# 7, которые были отправлены через шесть лет после написания этой статьи, являются типами значений и, следовательно, менее вероятны для создания давления в коллекции.
Ответ 2
Я думаю, что ответ зависит от семантики того, что выполняет функция, и от отношения между этими двумя значениями.
Например, методы TryParse
принимают параметр out
для принятия разобранного значения и возвращают bool
, чтобы указать, удалось ли выполнить синтаксический анализ. Эти два значения действительно не принадлежат друг другу, поэтому семантически это имеет смысл, и цель кода легче читать, чтобы использовать параметр out
.
Если, однако, ваша функция возвращает координаты X/Y некоторого объекта на экране, то два значения семантически принадлежат друг другу, и было бы лучше использовать struct
.
Я бы лично избегал использования tuple
для всего, что будет видно внешнему коду из-за неудобного синтаксиса для извлечения членов.
Ответ 3
Добавляя к предыдущим ответам, С# 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
Поддерживается также деструктуризация:
(string firstName, string lastName, int age) = getPerson()
Ответ 4
Пойду с подходом к использованию параметра Out, потому что во втором подходе вам понадобится создать и объект класса Tuple, а затем добавить к нему значение, которое, по моему мнению, является дорогостоящей операцией по сравнению с возвратом значения из параметра out. Хотя, если вы хотите вернуть несколько значений в Tuple Class (который не может быть достигнут, просто вернув один параметр out), я пошлю второй подход.
Ответ 5
Вы не указали еще один параметр, который имеет вместо класса тип пользовательского класса. Если у данных есть семантика, связанная с ним, на которую могут воздействовать функции, или размер экземпляра достаточно велик ( > 16 байт как правило), может быть предпочтительным пользовательский класс.
Использование "out" не рекомендуется в публичном API из-за его ассоциации с указателями и требует понимания того, как работают ссылочные типы.
https://msdn.microsoft.com/en-us/library/ms182131.aspx
Tuple хорош для внутреннего использования, но его использование неудобно в публичном API.
Итак, мой голос зависит от структуры и класса для публичного API.
Ответ 6
Нет "лучшей практики". Это то, с чем вам удобно, и что лучше всего работает в вашей ситуации. Если вы согласны с этим, нет никаких проблем с любыми решениями, которые вы опубликовали.