C#多重赋值

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

Adam Maras在评论中指出:

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

对于上面的示例有点起作用,但正如他所指出的那样,它创建了一个新的元组而不是更改指定的值。

Eric Lippert要求使用情况,因此也许:

(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也有一些使用案例,可以在他下面的回答中查看。


11
我觉得最后那句话很难读懂。 - BoltClock
1
我认为这样更易读:int a = 2; int b = 3; - JonH
1
@JonH:int a = 2,b = 3; 可选择将逗号后的空格替换为换行符。 - BoltClock
4
使用F#更适合处理这种事情。 - Nasreddine
1
嘿,我喜欢这个想法!特别是对于out变量! - Arlen Beiler
显示剩余4条评论
7个回答

23
我们曾考虑过为元组提供语法糖的支持,但它没有达到 C# 4.0 的标准。在 C# 5.0 中也不太可能实现;C# 5.0 团队正忙于确保异步/等待功能正常工作。我们将在假设的未来版本中考虑这个特性。
如果你有一个非常实用且令人信服的使用案例,那将帮助我们优先考虑这个特性。

6
如果使用多重赋值来重写具有“out”参数的大多数方法,将会更易读、更简洁。编译器警告“out参数‘x’在控制离开当前方法前必须被赋值”说明了使用out变量的错误风险——如果您改为在返回语句中指定所有返回变量,那么程序员就可以很好地自我加强,以确保他们正在做正确的事情。 - user87453
2
在我看来,经过一些Scala的尝试和阅读其他语言如何处理这个问题后,我现在认为C#元组的方法真的很便宜和丑陋。我非常期待看到这个特性在语言中得到完全实现,或者至少添加一些语法糖。就目前而言,对我来说它感觉像是一个半成品的特性。 - Trap
2
我们这些编程更偏向于数学的人非常欣赏语言中的多重赋值/元组支持。最近我大量使用了F#,因为它具备这个特性,只有在必须与COM代码交互的地方才使用C#,因为F#让我们可以将论文中的形式化转换成代码。我希望C#也能拥有这些功能,我的合作者们对函数式语言不太熟悉,他们也很想要这些功能。 - Dan Barowy
2
@DanBarowy:确实,F#经常因为这个特性而受到赞扬;相比之下,C#则有更多的“仪式感”。我已经不再有能力强烈影响假设未来版本的功能集,但我知道Anders、Mads和其他设计师都意识到这是一个痛点。 - Eric Lippert
1
"它没有达到C# 4.0的标准。""忙于确保异步/等待功能正常工作。" "双重困扰。" - nicolas

7

使用场景:

这对于与IObservable一起使用非常有用,因为它们只有一个类型参数。您基本上想要使用任意委托进行订阅,但是您被强制使用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
你所寻找的行为可以在支持或具有语法糖tuples的语言中找到。C#不属于这些语言;虽然您可以使用Tuple<...>类来实现类似的行为,但它会变得非常冗长(不像您要求的那样简洁)。

1
var tuple = Tuple.Create(2, 3); 这并不太糟糕。 - Dismissile
1
不过,它并不像许多函数式语言那样流畅地处理元组。 - Adam Maras
你能否使用元组和var重写第二个示例? - alan2here
2
var result = n == 4 ? Tuple.Create(2, 3) : Tuple.Create(3, n % 2 == 0 ? 1 : 2);(脑海中想到的,可能不完全正确) - Adam Maras
1
请注意,这与您的示例不一致,因为您正在创建一个新元组,而不是将ab视为隐式元组的一部分。然后,您需要将元组的每个部分的结果分配给ab,或者只是引用元组。 - Adam Maras


1

我能想到的最接近的结构是框架版本4.0中的Tuple类。


1

正如其他人所写的,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进行“逆向工程”。

显然,这有些牵强,每个从事严肃数值计算的人都应该有一个复杂类型(就像BCL中的那个)。但是,每当你获得分裂结果并且你想为了可读性给这些结果命名时,你需要元组拆包。重写后的最后两行将是:

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

这样就不会有任何混淆的余地,除了需要适应语法。


1
创建一组 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

这个方案不比你的复杂,而且更加清晰易懂。


2
首先,使用该技术时无法使用var来推断a和b的类型,其次它比(a, b) = (2, 3)要冗长得多。并不是说你的提议不好,只是它不如完全集成元组的语言那么好。 - Suzanne Soy
具有元组的内置支持的编程语言非常好用,但我不认为C#应该是这样的一种语言。在我看来,增加冗余性是可取的,因为任何具有基本C#语法理解(或生疏、不注意等)的程序员都可以立即直观地知道那行代码的含义。 - Buddy Gorven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接