39

This compiles correctly in C# 7.3 (Framework 4.8):

(string, string) s = ("a", "b");
(object, string) o = s;

I know that this is syntactic sugar for the following, which also compiles correctly:

ValueTuple<string, string> s = new ValueTuple<string, string>("a", "b");
ValueTuple<object, string> o = s;

So, it appears that ValueTuples can be assigned covariantly, which is awesome!

Unfortunately, I don't understand why: I was under the impression that C# only supported covariance on interfaces and delegates. ValueType is neither.

In fact, when I try to duplicate this feature with my own code, I fail:

struct MyValueTuple<A, B>
{
    public A Item1;
    public B Item2;

    public MyValueTuple(A item1, B item2)
    {
        Item1 = item1;
        Item2 = item2;
    }
}

...

MyValueTuple<string, string> s = new MyValueTuple<string, string>("a", "b");
MyValueTuple<object, string> o = s;
// ^ Cannot implicitly convert type 'MyValueTuple<string, string>' to 'MyValueTuple<object, string>'

So, why can ValueTuples be assigned covariantly, but MyValueTuples can't?

Heinzi
  • 167,459
  • 57
  • 363
  • 519

1 Answers1

29

I believe what is actually happening here is a destructuring assignment. Tuple assignment will try to implicitly convert its components, and as it is possible to assign string to object, that is what happens here.

The language supports assignment between tuple types that have the same number of elements, where each right-hand side element can be implicitly converted to its corresponding left-hand side element. Other conversions aren't considered for assignments.

Source

See it on sharplab.io

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • 4
    I just tried it on SharpLab and sure enough [it does exactly that](https://sharplab.io/#v2:D4AQTAjAsAUCDMACciDCiDetE+UkALIgLIAUAlJtrjaSBAAwA0yjlAzogLyKkBEAQz4s+AIz7kA3NRo5SAe1EArAKYBjAC4t6DSvO6J20mLNz0AnAqkzEAX1i2gA). – ProgrammingLlama Dec 19 '19 at 14:17
  • 3
    Just to reinforce that, in OP's original example, if you change `s` to type `(string, object)` it results in a conversion error, indicating that implicit conversion is taking place between the items, and string can be implicitly converted to string, but not vice versa. – Eric Lease Dec 19 '19 at 14:46