O que torna o ValueTuple covariante?

35

Isso é compilado corretamente no C # 7.3 (Framework 4.8):

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

Eu sei que isso é açúcar sintático para o seguinte, que também compila corretamente:

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

Portanto, parece que os ValueTuples podem ser atribuídos de forma covariável , o que é incrível !

Infelizmente, não entendo o motivo : fiquei com a impressão de que o C # só suportava covariância em interfaces e delegados . ValueTypenão é nenhum.

De fato, quando tento duplicar esse recurso com meu próprio código, falho:

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

Então, por que ValueTuples podem ser atribuídos covariantemente, mas MyValueTuples não?

Heinzi
fonte
2
Esse é provavelmente um tratamento especial do compilador, assim como você pode atribuir nulla um Nullable<T>mesmo que seja um struct.
juharr
2
Na verdade, o código descompilado fica assim para a segunda tarefa:ValueTuple<object, string> o = new ValueTuple<object, string>(s.Item1, s.Item2);
Lasse V. Karlsen
2
As tuplas são estranhas e são implementadas inteiramente no front end do compilador c #, em vez de depender de uma representação CLR subjacente. Esse operador de atribuição não está fazendo o que você pensa.
perfil completo de Jeremy Lakeman
2
Adicione um operador implícito para public static implicit operator MyValueTuple<A, B>(MyValueTuple<string, string> v) { throw new NotImplementedException(); }desconstruir a atribuição. Além disso, ótima pergunta, a propósito!
Çöđěxěŕ
11
@ Çöđěxěŕ olho de boi! que o torna compileable, e a exceção é lançada como esperado
Mong Zhu

Respostas:

25

Acredito que o que realmente está acontecendo aqui é uma tarefa de desestruturação. A atribuição de tupla tentará converter implicitamente seus componentes e, como é possível atribuir stringa objectisso, é o que acontece aqui.

O idioma suporta a atribuição entre tipos de tupla que possuem o mesmo número de elementos, onde cada elemento do lado direito pode ser implicitamente convertido em seu elemento correspondente do lado esquerdo. Outras conversões não são consideradas para atribuições.

Fonte

Veja em sharplab.io

Gareth Latty
fonte
4
Eu apenas tentei no SharpLab e com certeza faz exatamente isso .
John
3
Apenas para reforçar que, no exemplo original do OP, se você alterar spara digitar, (string, object)isso resulta em um erro de conversão, indicando que está ocorrendo uma conversão implícita entre os itens, e a string pode ser implicitamente convertida em string, mas não vice-versa.
Eric Lease