Возвращая два значения, Кортеж 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.
нет "лучшей практики". Это то, что вам удобно и что лучше всего работает в вашей ситуации. Пока вы согласны с этим, нет никаких проблем с любым из решений, которые вы опубликовали.