Was macht ValueTuple kovariant?

35

Dies wird in C # 7.3 (Framework 4.8) korrekt kompiliert:

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

Ich weiß, dass dies syntaktischer Zucker für Folgendes ist, der auch korrekt kompiliert wird:

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

Es scheint also, dass ValueTuples kovariant zugewiesen werden können , was großartig ist !

Leider verstehe ich nicht warum : Ich hatte den Eindruck, dass C # nur Kovarianz auf Schnittstellen und Delegaten unterstützt . ValueTypeist weder.

Wenn ich versuche, diese Funktion mit meinem eigenen Code zu duplizieren, scheitere ich tatsächlich:

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

Also, warum können ValueTuples kovariant zugewiesen werden, aber MyValueTuplenicht?

Heinzi
quelle
2
Dies ist wahrscheinlich eine besondere Behandlung durch den Compiler, wie , wie Sie zuweisen können , nullum ein , Nullable<T>obwohl es eine Struktur.
Juharr
2
Tatsächlich sieht der dekompilierte Code für die zweite Aufgabe so aus:ValueTuple<object, string> o = new ValueTuple<object, string>(s.Item1, s.Item2);
Lasse V. Karlsen
2
Tupel sind seltsam und werden vollständig im C # -Compiler-Frontend implementiert, anstatt sich auf eine zugrunde liegende CLR-Darstellung zu verlassen. Dieser Zuweisungsoperator macht nicht das, was Sie denken.
Jeremy Lakeman
2
Fügen Sie einen impliziten Operator hinzu, public static implicit operator MyValueTuple<A, B>(MyValueTuple<string, string> v) { throw new NotImplementedException(); }der die Zuweisung dekonstruiert. Auch tolle Frage übrigens!
Çöđěxěŕ
1
@ Çöđěxěŕ Volltreffer! das macht es kompilierbar, und die Ausnahme wird wie erwartet ausgelöst
Mong Zhu

Antworten:

25

Ich glaube, was hier tatsächlich passiert, ist eine destrukturierende Aufgabe. Tuple Zuordnung wird versuchen implizit auf seine Komponenten zu umwandeln, und wie es möglich ist , zuweisen stringzu object, das ist , was hier passiert.

Die Sprache unterstützt die Zuordnung zwischen Tupeltypen mit der gleichen Anzahl von Elementen, wobei jedes Element auf der rechten Seite implizit in das entsprechende Element auf der linken Seite konvertiert werden kann. Andere Conversions werden für Zuweisungen nicht berücksichtigt.

Quelle

Sehen Sie es auf sharplab.io

Gareth Latty
quelle
4
Ich habe es gerade auf SharpLab ausprobiert und sicher macht es genau das .
John
3
Nur um dies zu verstärken: Wenn Sie im ursprünglichen Beispiel von OP den sTyp ändern , (string, object)führt dies zu einem Konvertierungsfehler, der darauf hinweist, dass eine implizite Konvertierung zwischen den Elementen stattfindet und die Zeichenfolge implizit in eine Zeichenfolge konvertiert werden kann, aber nicht umgekehrt.
Eric Lease