Span Konvertierungen: plötzlich sind Arrays gefühlt Spans
Mit .NET 10 kommt C# 14, und damit ein Feature, das sich erst mal nach Komfort anfühlt: Methoden mit Span oder ReadOnlySpan Parametern werden bei der Überladungsauflösung viel häufiger als passend betrachtet. Das betrifft vor allem Extension Methods aus System.MemoryExtensions. Klingt harmlos, ist es auch meistens, nur eben nicht immer.
Die technische Idee dahinter ist simpel: Es gibt zusätzliche eingebaute Konvertierungen, die Arrays in Richtung Span und ReadOnlySpan bringen, und diese Konvertierungen werden stärker in Type Inference und Extension Method Lookup einbezogen. Dadurch kann ein Aufruf, der früher eindeutig war, plötzlich eine andere Zielmethode bekommen. Genau das beschreibt Microsoft auch als Verhaltensänderung in .NET 10.
Contains: derselbe Code, andere Methode, andere Laufzeit
Das prominenteste Beispiel ist array.Contains(x). Früher landete man oft bei Enumerable.Contains. Mit C# 14 kann stattdessen MemoryExtensions.Contains gebunden werden, weil es Overloads für Span und ReadOnlySpan gibt und die neuen Regeln die Bindung ermöglichen.
Im normalen Codepfad merkst du davon selten etwas. In Expression Trees aber schon. Microsoft weist explizit darauf hin, dass diese neue Bindung in Expression Lambdas zu Runtime Exceptions führen kann, speziell wenn die Lambda interpretiert kompiliert wird. JetBrains warnt in Rider und ReSharper ebenfalls vor genau diesem Szenario.
Warum? Expression Trees sind kein vollwertiger IL Dump. Sie modellieren nur einen Teil der Sprache. Und ref struct Typen wie Span sind in diesem Modell ein schwieriger Gast. Sobald eine Expression plötzlich eine spanbasierte Extension Method enthält, wird aus einer sauberen Query oder Predicate schnell ein Laufzeitproblem.
Der eigentliche Kniff: op_Implicit taucht als Call im Tree auf
Jetzt zum spannenden Teil, der gern übersehen wird: Die Konvertierung Array zu Span erscheint in Expression Trees oft als Call auf einen speziellen impliziten Operator. Das ist eine statische Methode mit SpecialName op_Implicit auf einem generischen DeclaringType. Und genau da setzt der Visitor Fix an:
|
|
Was passiert hier fachlich? Du erkennst gezielt die implizite Span Conversion und behandelst sie als No op für die Übersetzung. Das ist keine Schlamperei, das ist Absicht: Für viele Query Provider zählt die semantische Quelle, also das Array oder eine Collection, nicht das spanbasierte Zwischenstück.
Das ist auch der Grund, weshalb sich das wie ein kleiner Trick anfühlt, aber in Wahrheit eine saubere Normalisierung ist. Du bringst den Tree zurück in eine Form, die dein Translator sicher versteht.
Ausblick: Fix ist mehr als Kosmetik
Dieser Visitor Schritt allein löst noch nicht jedes Contains Problem, aber er ist ein stabiler Baustein. In der Praxis kombinierst du ihn oft mit einer zweiten Regel: Wenn Contains auf MemoryExtensions gebunden wurde, leite aktiv auf Enumerable.Contains oder eine providerfreundliche Variante. Der Kernpunkt bleibt: Seit .NET 10 kann dieselbe Zeile Code eine andere Methode targeten, und Expression basierte Systeme spüren das sofort.