Что делает ValueTuple ковариантным?

35

Это правильно компилируется в C # 7.3 (Framework 4.8):

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

Я знаю, что это синтаксический сахар для следующего, который также правильно компилируется:

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

Итак, похоже, что ValueTuples могут быть назначены ковариантно , что потрясающе !

К сожалению я не понимаю почему : у меня сложилось впечатление, что C # поддерживает ковариацию только для интерфейсов и делегатов . ValueTypeэто ни то, ни другое

Фактически, когда я пытаюсь продублировать эту функцию собственным кодом, у меня не получается:

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>'

Итак, почему ValueTuples можно назначать ковариантно, ноMyValueTuple s нет?

Heinzi
источник
2
Скорее всего, это особая обработка компилятором, точно так же, как вы можете назначить nullдля него Nullable<T>даже если это структура.
juharr
2
На самом деле, декомпилированный код выглядит следующим образом для второго назначения:ValueTuple<object, string> o = new ValueTuple<object, string>(s.Item1, s.Item2);
Лассе В. Карлсен
2
Кортежи странные и реализованы полностью во внешнем интерфейсе компилятора c # вместо того, чтобы полагаться на базовое представление CLR. Этот оператор присваивания не делает то, что вы думаете.
Джереми
2
Добавьте неявный оператор public static implicit operator MyValueTuple<A, B>(MyValueTuple<string, string> v) { throw new NotImplementedException(); }его деконструкции присваивания. Кроме того, отличный вопрос, кстати!
Çöđěxěŕ
1
@ Çöđěxěŕ бычий глаз! что делает его компилируемым, и исключение выдается, как и ожидалось
Монг Чжу

Ответы:

25

Я считаю, что то, что на самом деле здесь происходит, является деструктивным заданием. Назначение Кортеж будет пытаться неявно преобразовать его компоненты, и как можно назначить stringдляobject , это то , что здесь происходит.

Язык поддерживает присваивание между типами кортежей, которые имеют одинаковое количество элементов, где каждый элемент правой части может быть неявно преобразован в соответствующий ему элемент левой части. Другие преобразования не рассматриваются для заданий.

Источник

Смотрите это на sharplab.io

Гарет Латти
источник
4
Я только что попробовал это на SharpLab и уверен, что он делает именно это .
Джон
3
Просто чтобы подчеркнуть, что в оригинальном примере OP, если вы измените sтип, (string, object)это приведет к ошибке преобразования, указывающей на то, что неявное преобразование происходит между элементами, и строка может быть неявно преобразована в строку, но не наоборот.
Эрик Лиз