¿Qué hace que ValueTuple sea covariante?

35

Esto se compila correctamente en C # 7.3 (Framework 4.8):

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

Sé que esto es azúcar sintáctico para lo siguiente, que también se compila correctamente:

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

Por lo tanto, parece que ValueTuples se puede asignar covariantly , que es impresionante !

Desafortunadamente, no entiendo por qué : tenía la impresión de que C # solo admitía la covarianza en interfaces y delegados . ValueTypeEs ninguno.

De hecho, cuando trato de duplicar esta función con mi propio código, fallo:

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

Entonces, ¿por qué se ValueTuplepueden asignar s covariablemente, pero MyValueTupleno se pueden asignar s?

Heinzi
fuente
2
Este es probablemente un tratamiento especial por parte del compilador, al igual que la forma en que puede asignar nulla un Nullable<T>archivo aunque sea una estructura.
juharr
2
En realidad, el código descompilado se ve así para la segunda asignación:ValueTuple<object, string> o = new ValueTuple<object, string>(s.Item1, s.Item2);
Lasse V. Karlsen
2
Las tuplas son extrañas y se implementan completamente en el front-end del compilador de C #, en lugar de depender de una representación CLR subyacente. Ese operador de asignación no está haciendo lo que piensas.
Jeremy Lakeman
2
Agregue un operador implícito public static implicit operator MyValueTuple<A, B>(MyValueTuple<string, string> v) { throw new NotImplementedException(); }su deconstrucción de la asignación. Además, una gran pregunta por cierto!
Çöđěxěŕ
1
@ Çöđěxěŕ ojo de buey! eso lo hace compilable, y la excepción se produce como se esperaba
Mong Zhu

Respuestas:

25

Creo que lo que realmente está sucediendo aquí es una tarea de desestructuración. Asignación de tuplas tratará de convertir implícitamente sus componentes, y como es posible asignar stringa object, que es lo que sucede aquí.

El lenguaje admite la asignación entre tipos de tuplas que tienen el mismo número de elementos, donde cada elemento del lado derecho se puede convertir implícitamente en su correspondiente elemento del lado izquierdo. No se consideran otras conversiones para las tareas.

Fuente

Véalo en sharplab.io

Gareth Latty
fuente
44
Lo probé en SharpLab y, efectivamente , hace exactamente eso .
John
3
Solo para reforzar eso, en el ejemplo original de OP, si cambia spara escribirlo, (string, object)se produce un error de conversión, lo que indica que se está produciendo una conversión implícita entre los elementos y que la cadena se puede convertir implícitamente en cadena, pero no al revés.
Eric Lease