У меня есть такой метод:
private double GetHeight()
{
return 2;
}
Но я хотел бы иметь возможность вернуть два разных числа, например 2 и 3. Можно ли как-то сделать это на С#?
для ясности: всегда ли ваш метод возвращает такое же количество возвращаемых значений (каждое с определенным семантическим значением) - или количество возвращаемых значений динамичный, т.е. иногда это будет 1 (или, может быть, даже ноль), а иногда это может быть 20? Это очень важно. Для сценария с фиксированным числом: любой из кортежных ответов хорош; для динамического сценария: подходы массива/списка хороши.
Откуда у этого вопроса столько голосов? Вопрос требует значительных усилий, и простой поиск в Интернете дал бы ответ stackoverflow.com/help/как спросить.
Да ValueTuple
/Именованный кортеж (доступно в C# 7.0). Преимущество в том, что он самый лаконичный, неизменяемый и простой в построении.
The ValueTuple struct has fields named Item1, Item2, Item3, and so on, similar to the properties defined in the existing Tuple types.
However, when you initialize a tuple, you can use new language features that give better names to each field. Doing so creates a named tuple. Named tuples still have elements named Item1, Item2, Item3 and so on. But they also have synonyms for any of those elements that you have named. You create a named tuple by specifying the names for each element.
private (double first, double second) GetHeight()
{
return (1,2);
}
...
var result = ViaNamedValueTuple();
Console.WriteLine($"{result.first}, {result.second}");
var (first, second) = ViaNamedValueTuple();
Console.WriteLine($"{first}, {second}");
Классический кортеж
The .NET Framework already has generic Tuple classes. These classes, however, had two major limitations. For one, the Tuple classes named their properties Item1, Item2, and so on. Those names carry no semantic information. Using these Tuple types does not enable communicating the meaning of each of the properties. The new language features enable you to declare and use semantically meaningful names for the elements in a tuple.
public Tuple<int, int> ViaClassicTuple()
{
return new Tuple<int, int>(1,2);
}
...
var tuple = ViaClassicTuple();
Console.WriteLine($"{tuple.Item1}, {tuple.Item2}");
Классическая структура
A struct type is a value type that is typically used to encapsulate small groups of related variables, such as the coordinates of a rectangle or the characteristics of an item in an inventory.
public struct ClassicStruct
{
public int First { get; set; }
public int Second { get; set; }
public ClassicStruct(int first, int second)
{
First = first;
Second = second;
}
}
...
public ClassicStruct ViaClassicStruct()
{
return new ClassicStruct(1, 2);
}
...
var classicStruct = ViaClassicStruct();
Console.WriteLine($"{classicStruct.First}, {classicStruct.Second}");
Структура только для чтения
только для чтения (справочник по C#)
The readonly modifier on a struct definition declares that the struct is immutable. Every instance field of the struct must be marked readonly, as shown in the following example:
public readonly struct ReadonlyStruct
{
public int First { get; }
public int Second { get; }
public ReadonlyStruct(int first, int second)
{
First = first;
Second = second;
}
}
...
public ReadonlyStruct ViaReadonlyStruct()
{
return new ReadonlyStruct(1, 2);
}
...
var readonlyStruct = ViaReadonlyStruct();
Console.WriteLine($"{readonlyStruct.First}, {readonlyStruct.Second}");
Простой класс
Классы (Руководство по программированию на C#)
A type that is defined as a class is a reference type. At run time, when you declare a variable of a reference type, the variable contains the value null until you explicitly create an instance of the class by using the new operator, or assign it an object of a compatible type that may have been created elsewhere
public class SomeClass
{
public int First { get; set; }
public int Second { get; set; }
public SomeClass(int first, int second)
{
First = first;
Second = second;
}
}
...
public SomeClass ViaSomeClass()
{
return new SomeClass(1, 2);
}
...
var someClass = ViaSomeClass();
Console.WriteLine($"{someClass.First}, {someClass.Second}");
Выходные параметры
модификатор параметра out (справочник по C#)
The out keyword causes arguments to be passed by reference. It makes the formal parameter an alias for the argument, which must be a variable. In other words, any operation on the parameter is made on the argument. It is like the ref keyword, except that ref requires that the variable be initialized before it is passed. It is also like the in keyword, except that in does not allow the called method to modify the argument value. To use an out parameter, both the method definition and the calling method must explicitly use the out keyword.
public bool ViaOutParams(out int first, out int second)
{
first = 1;
second = 2;
return someCondition;
}
...
if (ViaOutParams(out var firstInt, out var secondInt))
Console.WriteLine($"{firstInt}, {secondInt}");
Исходящий кортеж значений
public bool ViaOutTuple(out (int first,int second) output)
{
output = (1, 2);
return someCondition;
}
...
if (ViaOutTuple(out var output))
Console.WriteLine($"{output.first}, {output.second}");
Примечание. Доступно в C# 7.0+.
Возможно, структура была бы более подходящей, чем класс, если намерение состоит в том, чтобы сохранить передачу значений.
Создайте массив int. Или другой способ - создать класс.
массив здесь определенно плохой способ - это довольно выделяет а также семантическое значение каждого значения неясно; создание сорт также было бы относительно распределенным, но... readonly struct
было бы хорошо
Это неплохо, но в текущем вопросе это не нужно.
Я бы предпочел иметь класс строго типизированных значений, которые что-то значат, а не угадывать, сколько их в массиве и что представляют их значения.
Вы тоже можете составить список.
@VukUskokovic, у которого (список) есть все проблемы, которые есть у массивов, плюс дополнительный объект и уровень абстракции; создание списка просто усугубляет проблема, если только мы не являемся семантически возвращение списка чего-либо
Мой плохой, это неплохой способ, я думаю, что есть более простые способы.
@MarcGravell, я согласен, но вы можете составить список того, сколько целых чисел он хочет, чтобы он не возвращал только 2, вот как я получил вопрос.
@VukUskokovic достаточно честно - вопрос неясен и неоднозначен; если намерение по сути является произвольным методом List<double> GetHeights()
, то да: это будет решение хорошо (оно показывает, насколько важен четкий контекст, который является проблемой вопроса, а не вашего ответа)
@VukUskokovic Я добавил комментарий/запрос к вопросу
@MarcGravell, спасибо.
Несколько способов:
out
параметры:
private double GetHeight(out int anotherValue)
{
anotherValue = 42;
return 2;
}
значения-кортежи:
private (double height, int anotherValue) GetHeight()
{
return (42, 2);
}
(предупреждение: кортежи значений имеют известные проблемы при использовании в библиотеках .NET Standard, поскольку привязки сборок из .NET Standard в .NET Framework ... своего рода фубар)
настраиваемые типы возврата:
private Something GetHeight()
{
return new Something(42, 2);
}
(чтобы избежать распределения, вы можете определить Something
как readonly struct
в этом сценарии)
Также вы можете использовать параметры out
:
static void Main(string[] args)
{
int i, j;
GetHeight(out i, out j);
}
public static void GetHeight(out int i1, out int i2)
{
i1 = 1;
i2 = 2;
}
Да, вы можете использовать Кортеж:
class Program
{
static void Main(string[] args)
{
Tuple<int, int> height = GetHeight();
Console.WriteLine(height.Item1 + " - " + height.Item2);
Console.ReadLine();
}
private static Tuple<int, int> GetHeight()
{
return new Tuple<int, int>(2, 3);
}
}
ВЫХОД:
2 - 3
вернуть массив, если вам нужно больше значений