c # мульти назначение - PullRequest
       14

c # мульти назначение

16 голосов
/ 28 ноября 2011
int a, b, n;
...
(a, b) = (2, 3);
// 'a' is now 2 and 'b' is now 3

Подобные вещи были бы действительно полезны в C #. В этом примере «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;

Ноталлама также имеет варианты использования, они в его ответе ниже.

Ответы [ 7 ]

21 голосов
/ 28 ноября 2011

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

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

7 голосов
/ 04 декабря 2011

вариант использования:

было бы неплохо работать с 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)>

будет десугар до

IEnumerator<Tuple<int,int>>
3 голосов
/ 28 ноября 2011

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

1 голос
/ 11 мая 2017
1 голос
/ 07 апреля 2014

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

Ваш пример станет

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

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

1 голос
/ 01 августа 2012

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

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);

Не оставляет ни малейшего понятия о том, что это решение на самом деле (хорошо, строка и имя метода делают это довольно очевидным) на сайте вызова. Item1 и Item2 одного типа, что делает подсказки бесполезными. Единственный способ узнать наверняка - это «перепроектировать» свой путь обратно через SolvePQ.

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

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

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

1 голос
/ 28 ноября 2011

Самая близкая структура, которую я могу придумать, это класс Tuple в версии 4.0 платформы.

...