Given a function (string a, string b) F()
, you can deconstruct the tuple it returns:
var (a, b) = F();
(string c, string d) = F();
Or you can just assign it:
var (a, b) e = F();
(string a, string b) f = F();
var g = F(); // One of these things is not like the others.
Class deconstructors behave like the first case. Given a class C
with Deconstructor(out string a, out string b)
:
var c = new C();
var (h, i) = c;
(string j, string k) = c;
But the compiler won't use the deconstructor to implicitly convert it to a tuple:
// Cannot implicitly convert type 'C' to '(string a, string b)'
var (a, b) l = c;
Obviously you can mechanically write an implicit conversion based on the deconstructor:
public static implicit operator (string a, string b) (C c)
{
c.Deconstruct(out string a, out string b);
return (a, b);
}
Notwithstanding the visual similarity in the syntax between the deconstruction and assignment cases, assigning a reference to a tuple is not the same as deconstructing a class into variables and then putting them in a new tuple. However, you can implicitly convert (int x, int y)
to (double x, double y)
. Value tuples are the kind of syntactic-sugar feature where it does what it looks like it does, and never mind the implementation details.
If I thought of this, the C# team thought of it, and if they chose not to add "magic" support for the implicit conversion, they had a good reason1.
Is there a positive reason why doing the implicit conversion automatically would have been a bad idea?
Or is it one of those features that just wasn't regarded as valuable enough to justify the cost?
Here's the code from that fiddle:
public class Program
{
public static void Main()
{
(string a, string b) = F();
(string a, string b) ab = F();
Console.WriteLine($"a: {a} b: {b} ab: {ab}");
var c = new C();
(string d, string e) = c;
// Cannot implicitly convert type 'C' to '(string a, string b)'
(string a, string b) f = c;
Console.WriteLine($"d: {d} e: {e} f: {f}");
// Covariance
(object c, object d) g = F();
// Implicit conversion
(double x, double y) t = G();
}
public static (string a, string b) F()
=> ("A", "B");
public static (int x, int y) G()
=> (0, 1);
}
public class C
{
public String A = "A";
public String B = "B";
public void Deconstruct(out String a, out String b)
{
a = A;
b = B;
}
}
1 The C# team may not be smarter than everybody, but I've never lost money betting they were at least as smart as me.