Возвращаются два значения, Кортеж против 'из' против 'структура'


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

// 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 61

6 ответов:

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

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

кортежи делают коллекцию давления и являются самодокументирующимися. "Item1" не очень описателен.

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

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

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

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

например,TryParse методы взять out параметр, чтобы принять анализируемое значение и вернуть a 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

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

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

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

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

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

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

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