Подтвердить что ты не робот

Назначение С# multi

int a, b, n;
...
(a, b) = (2, 3);
// 'a' is now 2 and 'b' is now 3

Такая вещь была бы очень полезна в С#. В этом примере "a" и "b" объединены вместе, такие как X и Y позиции могут быть. Это существует в той или иной форме?

Ниже представлен менее тривиальный пример.

(a, b) = n == 4 ? (2, 3) : (3, n % 2 == 0 ? 1 : 2);

Адам Марас показывает в комментариях, что:

var result = n == 4 ? Tuple.Create(2, 3) : Tuple.Create(3, n % 2 == 0 ? 1 : 2);

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

Эрик Липперт просит использовать прецеденты, поэтому возможно:

(a, b, c) = (c, a, b); // swap or reorder on one line
(x, y) = move((x, y), dist, heading);
byte (a, b, c, d, e) = (5, 4, 1, 3, 2);
graphics.(PreferredBackBufferWidth, PreferredBackBufferHeight) = 400;

notallama также имеет прецеденты, они находятся в его ответе ниже.

4b9b3361

Ответ 1

Мы рассмотрели поддержку синтаксического сахара для кортежей, но он не сделал планку для С# 4.0. Маловероятно сделать планку для С# 5.0; команда С# 5.0 довольно занята тем, что получает асинхронный/ждущий работу. Мы рассмотрим его для гипотетических будущих версий языка.

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

Ответ 2

прецедент:

было бы очень приятно работать с IObservables, так как у них есть только один параметр типа. вы в основном хотите подписаться с произвольными делегатами, но вы вынуждены использовать Action, поэтому это означает, что если вам нужно несколько параметров, вам нужно либо использовать кортежи, либо создавать пользовательские классы для параметров упаковки и распаковки.

пример из игры:

public IObservable<Tuple<GameObject, DamageInfo>> Damaged ...

void RegisterHitEffects() {
    (from damaged in Damaged
     where damaged.Item2.amount > threshold
     select damaged.Item1)
    .Subscribe(DoParticleEffect)
    .AddToDisposables();
}

становится:

void RegisterHitEffects() {
    (from (gameObject, damage) in Damaged
     where damage.amount > threshold
     select gameObject)
    .Subscribe(DoParticleEffect)
    .AddToDisposables();
}

который я считаю более чистым.

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

тоже было бы неплохо, если бы сахар работал и на общие параметры. так:

IEnumerator<(int, int)>

будет desugar для

IEnumerator<Tuple<int,int>>

Ответ 3

Поведение, которое вы ищете, можно найти на языках с поддержкой или синтаксическим сахаром для кортежей. С# не входит в число этих langauges; в то время как вы можете использовать классы Tuple<...> для достижения аналогичного поведения, он выйдет очень многословным (не чистым, как вы ищите).

Ответ 4

Ближайшей структурой, о которой я могу думать, является класс Tuple в версии 4.0 фреймворка.

Ответ 5

Как уже писали другие, С# 4 Tuples - отличное дополнение, но ничто действительно не пригодится для использования, пока нет каких-либо механизмов распаковки. То, что я действительно требую от любого типа, который я использую, - это ясность того, что он описывает, с обеих сторон протокола функции (например, вызывающего абонента, стороны каллуса)... например

Complex SolvePQ(double p, double q)
{
    ...
    return new Complex(real, imag);
}
...
var solution = SolvePQ(...);
Console.WriteLine("{0} + {1}i", solution.Real, solution.Imaginary);

Это очевидно и понятно как на стороне абонента, так и на стороне абонента. Однако это

Tuple<double, double> SolvePQ(double p, double q)
{
    ...
    return Tuple.Create(real, imag);
}
...
var solution = SolvePQ(...);
Console.WriteLine("{0} + {1}i", solution.Item1, solution.Item2);

Не оставляет ни малейшего понятия о том, что такое решение на самом деле (нормально, строка и имя метода делают это довольно очевидным) на сайте вызова. Элементы 1 и 2 имеют тот же тип, что делает всплывающие подсказки бесполезными. Единственный способ узнать наверняка - это "перепроектировать" свой путь назад через SolvePQ.

Обидно, это далека, и каждый, кто делает серьезный численный материал, должен иметь сложный тип (например, в BCL). Но каждый раз, когда вы получаете результаты сплит, и вы хотите дать этим результатам разные имена для удобства чтения, вам необходимо распаковать кортеж. Переписанные последние две строки:

var (real, imaginary) = SolvePQ(...); // or var real, imaginary = SolvePQ(...);
Console.WriteLine("{0} + {1}i", real, imaginary);

Это не оставляет места для путаницы, кроме привыкания к синтаксису.

Ответ 6

Создание набора методов Unpack<T1, T2>(this Tuple<T1, T2>, out T1, out T2) было бы более идиоматичным способом С#.

Затем ваш пример станет

int a, b, n;
...
Tuple.Create(2, 3).Unpack(out a, out b);
// 'a' is now 2 and 'b' is now 3

который не является более сложным, чем ваше предложение, и намного понятнее.