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

This sort of thing would be really helpfull in C#. In this example 'a' and 'b' arn't encapsulated together such as the X and Y of a position might be. Does this exist in some form?

Below is a less trivial example.

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

Adam Maras shows in the comments that:

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

Sort of works for the example above however as he then points out it creates a new truple instead of changing the specified values.

Eric Lippert asks for use cases, therefore perhaps:

(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 also has use cases, they are in his answer below.

9
  • 11
    The last statement looks really unreadable to me.
    – BoltClock
    Commented Nov 28, 2011 at 17:40
  • 1
    I think this is more readable int a = 2; int b = 3;
    – JonH
    Commented Nov 28, 2011 at 17:42
  • 1
    @JonH: int a = 2, b = 3; optionally replacing the space after the comma with a line break.
    – BoltClock
    Commented Nov 28, 2011 at 17:42
  • 4
    Use F#, it's more suited for this kind of things
    – Nasreddine
    Commented Nov 28, 2011 at 17:47
  • 1
    Hey, I like this idea! Especially for out vars! Commented May 9, 2013 at 19:40

7 Answers 7

23

We have considered supporting a syntactic sugar for tuples but it did not make the bar for C# 4.0. It is unlikely to make the bar for C# 5.0; the C# 5.0 team is pretty busy with getting async/await working right. We will consider it for hypothetical future versions of the language.

If you have a really solid usage case that is compelling, that would help us prioritize the feature.

5
  • 6
    Most methods that use 'out' parameters would be more readable and concise if they were re-written using multiple assignment. The compiler warning "The out parameter 'x' must be assigned to before control leaves the current method" indicates how error-prone out vars can be—if you instead had to specify all the return vars in the return statement that would be a nice, self-reinforcing way for programmers to know they're doing the right thing.
    – user87453
    Commented Mar 26, 2012 at 0:12
  • 2
    IMHO, after playing with Scala a bit and having read about how other languages deal with this, I now find the C# approach on tuples really cheap and ugly. I'm really looking forward to see this feature fully implemented in the language or at least some syntactic sugar added. As it stands now it feels like a half baked feature to me.
    – Trap
    Commented Oct 15, 2012 at 11:42
  • 2
    Those of us whose programming is more mathematical in nature very much appreciate multiple assignment / tuple support in the language. I recently made extensive use of F# because of this, using only C# in places where I had to interact with COM code, because F# let us more or less transliterate the formalisms from our paper straight into code. I would love to have these features in C#, as would my collaborators who are less comfortable in functional languages than I am.
    – Dan Barowy
    Commented Jul 19, 2013 at 21:11
  • 2
    @DanBarowy: Indeed, F# is often praised for exactly this property; C# by contrast has much more "ceremony" around it. I am no longer in a position to strongly influence the feature set for hypothetical future versions, but I do know that Anders and Mads and the other designers are aware that this is a pain point. Commented Jul 19, 2013 at 22:39
  • 1
    "it did not make the bar for C# 4.0". "pretty busy with getting async/await working right". double baffle.
    – nicolas
    Commented Oct 16, 2013 at 14:30
7

use case:

it'd be really nice for working with IObservables, since those have only one type parameter. you basically want to subscribe with arbitrary delegates, but you're forced to use Action, so that means if you want multiple parameters, you have to either use tuples, or create custom classes for packing and unpacking parameters.

example from a game:

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

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

becomes:

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

which i think is cleaner.

also, presumably IAsyncResult will have similar issues when you want to pass several values. sometimes it's cumbersome to create classes just to shuffle a bit of data around, but using tuples as they are now reduces code clarity. if they're used in the same function, anonymous types fit the bill nicely, but they don't work if you need to pass data between functions.

also, it'd be nice if the sugar worked for generic parameters, too. so:

IEnumerator<(int, int)>

would desugar to

IEnumerator<Tuple<int,int>>
3

The behavior that you're looking for can be found in languages that have support or syntactic sugar for tuples. C# is not among these langauges; while you can use the Tuple<...> classes to achieve similar behavior, it will come out very verbose (not clean like you're looking for.)

5
  • 1
    var tuple = Tuple.Create(2, 3); It's not too terrible.
    – Dismissile
    Commented Nov 28, 2011 at 17:51
  • 1
    No, but it's not as fluent as (for example) a lot of functional languages make working with tuples.
    – Adam Maras
    Commented Nov 28, 2011 at 17:54
  • Can you rewrite the 2nd example using tuple and var?
    – alan2here
    Commented Nov 28, 2011 at 17:58
  • 2
    var result = n == 4 ? Tuple.Create(2, 3) : Tuple.Create(3, n % 2 == 0 ? 1 : 2); (off the top of my head, might not be quite right)
    – Adam Maras
    Commented Nov 28, 2011 at 18:06
  • 1
    Mind you, that's not congruent to your sample, as you're creating a new tuple instead of considering a and b as parts of an implicit tuple. You'd then have to assign the result of each part of the tuple to a and b afterwards, or just reference the tuple instead.
    – Adam Maras
    Commented Nov 28, 2011 at 18:08
2

Deconstruction was introduced in C# 7.0: https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/#user-content-deconstruction

0
1

The closest structure I can think of is the Tuple class in version 4.0 of the framework.

0
1

As others already wrote, C# 4 Tuples are a nice addition, but nothing really compelling to use as long as there aren't any unpacking mechanisms. What I really demand of any type I use is clarity of what it describes, on both sides of the function protocol (e.g. caller, calle sides)... like

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

This is obvious and clear at both caller and callee side. However this

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

Doesn't leave the slightest clue about what that solution actually is (ok, the string and the method name make it pretty obvious) at the call site. Item1 and Item2 are of the same type, which renders tooltips useless. The only way to know for certain is to "reverse engineer" your way back through SolvePQ.

Obivously, this is far fetched and everyone doing serious numerical stuff should have a Complex type (like that in the BCL). But everytime you get split results and you want give those results distinct names for the sake of readability, you need tuple unpacking. The rewritten last two lines would be:

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

This leaves no room for confusion, except for getting used to the syntax.

1

Creating a set of Unpack<T1, T2>(this Tuple<T1, T2>, out T1, out T2) methods would be a more idiomatic c# way of doing this.

Your example would then become

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

which is no more complex than your proposal, and a lot clearer.

2
  • 2
    First, with that technique you can't use var to infer the type of a and b, and secondly it's a whole lot more verbose than just (a, b) = (2, 3). Not saying your proposal is bad, just that it's not as good as what a full language integration of tuples could be. Commented Jun 30, 2014 at 14:56
  • Languages with integral support for tuples are really nice to work with, but I don't believe that c# should be such a language. In my opinion, the added verbosity is desirable, because any programmer with even a basic understanding of c# syntax (or rusty, inattentive, etc) instantly and intuitively knows what that line means.
    – Ivy Gorven
    Commented Jul 13, 2014 at 10:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.