Qu'est-ce qui rend covariant ValueTuple?

35

Cela se compile correctement en C # 7.3 (Framework 4.8):

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

Je sais que c'est du sucre syntaxique pour les éléments suivants, qui se compile également correctement:

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

Il semble donc que les ValueTuples puissent être attribués de manière covariante , ce qui est génial !

Malheureusement, je ne comprends pas pourquoi : j'avais l'impression que C # ne supportait que la covariance sur les interfaces et les délégués . ValueTypeest ni.

En fait, lorsque j'essaie de dupliquer cette fonctionnalité avec mon propre code, j'échoue:

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

Alors, pourquoi peut- ValueTupleon attribuer s de façon covariante, mais MyValueTuplepas?

Heinzi
la source
2
Il s'agit probablement d'un traitement spécial par le compilateur, tout comme la façon dont vous pouvez attribuer nullà un Nullable<T>même s'il s'agit d'une structure.
juharr
2
En fait, le code décompilé ressemble à ceci pour la deuxième affectation:ValueTuple<object, string> o = new ValueTuple<object, string>(s.Item1, s.Item2);
Lasse V. Karlsen
2
Les tuples sont étranges et sont implémentés entièrement dans le frontal du compilateur c #, au lieu de s'appuyer sur une représentation CLR sous-jacente. Cet opérateur d'affectation ne fait pas ce que vous pensez.
Jeremy Lakeman
2
Ajoutez un opérateur implicite public static implicit operator MyValueTuple<A, B>(MyValueTuple<string, string> v) { throw new NotImplementedException(); }pour déconstruire l'affectation. Aussi, bonne question d'ailleurs!
Çöđěxěŕ
1
@ Çöđěxěŕ bulls eye! qui le rend compilable, et l'exception est levée comme prévu
Mong Zhu

Réponses:

25

Je crois que ce qui se passe ici est une mission de déstructuration. Cession tuple tentera de convertir implicitement ses composants, et comme il est possible d'assigner stringà object, qui est ce qui se passe ici.

Le langage prend en charge l'affectation entre les types de tuple qui ont le même nombre d'éléments, où chaque élément de droite peut être implicitement converti en son élément de gauche correspondant. Les autres conversions ne sont pas prises en compte pour les affectations.

La source

Voir sur sharplab.io

Gareth Latty
la source
4
Je viens de l'essayer sur SharpLab et bien sûr, il fait exactement cela .
John
3
Juste pour renforcer cela, dans l'exemple d'origine de OP, si vous changez sde type, (string, object)cela entraîne une erreur de conversion, indiquant qu'une conversion implicite a lieu entre les éléments, et la chaîne peut être implicitement convertie en chaîne, mais pas l'inverse.
Eric Lease