Frage

Lassen Sie uns den MSIL-Code für die folgende generische Methode erzeugt untersuchen:

public static U BoxValue<T, U>(T value)
  where T : struct, U
  where U : class
{
  return value;
}

Look:

.method public hidebysig static !!U  BoxValue<valuetype .ctor
 ([mscorlib]System.ValueType, !!U) T,class U>(!!T 'value') cil managed
{
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  unbox.any  !!U
  IL_000b:  ret
}

Aber für generischen Code oben, desto effizient IL Darstellung sollte sein:

  IL_0000:  ldarg.0
  IL_0001:  box        !!T
  IL_0006:  ret

Es ist von den Beschränkungen bekannt, dass der Wert in ist eingerahmt Referenztyp . Unbox.any opcode ist vollständig redundant, weil nach box Opcode der Wert an IL-Stack wird bereits eine gültige Referenz sein, um !!U die ohne unboxing verwendet werden kann.

Warum C # 3.0-Compiler verwenden Einschränkungen Metadaten nicht effizienten generischen Code zu emittieren? Unbox.any gibt einen kleinen Overhead (nur 4-fach-5-fach langsamer), aber warum nicht besser Code in diesem Szenario emittiert?

War es hilfreich?

Lösung

Es sieht aus wie der Compiler tut dies wegen einiger Probleme mit dem Prüfer.

Die IL, dass Sie den Compiler möchten ist generieren nicht nachprüfbar, und so der C # -Compiler es nicht erzeugen kann (alle C # -Code außerhalb von „unsicheren“ Kontexte überprüfbar sein sollen).

Die Regeln für die "Überprüfung Typkompatibilität" in angegebenen Abschnitt 1.8.1.2.3, Partion III der Ecma spec.

Sie sagen, dass ein Typ ‚S‘ ist die Verifikation kompatibel mit einem Typ ‚T‘ oder (S: = T) mit den folgenden Regeln:

  1. [: = reflexiv] Für alle Verifikations Typen S, S = S
  2. [: = transitiv] Für alle Verifikations Typen S, T und U, wenn S: = T und T: = U, dann. S: = U
  3. . S: = T, wenn S ist die Basisklasse von T oder eine von T und T implementierte Schnittstelle ist kein Werttyp
  4. Objekt:. = T, wenn T ein Interface-Typ ist
  5. S: = T, wenn S und T beiden Schnittstellen sind und die Umsetzung von T erfordert die Implementierung von S
  6. S: = null, wenn S ein Objekttyp oder eine Schnittstelle ist
  7. S []: = T [], wenn S: = T und die Arrays entweder beide Vektoren (Null basierenden, Rang eins) oder keines von beidem ein Vektor ist und beide haben den gleichen Rang. (Diese Regel befasst sich mit Matrixkovarianz.)
  8. Wenn S und T sind Methodenzeiger, dann S: = T, wenn die Signaturen (Rückgabetypen, Parametertypen und Aufrufkonvention) sind gleich.

Von diesen Regeln, die einzigen, die in diesem Fall anwendbar sein könnten, ist # 3.

Allerdings # 3 nicht auf den Code anwenden, weil ‚U‘ nicht eine Basisklasse von ‚T‘ ist, und es ist keine Basis Schnittstelle von ‚T‘, so die ‚oder‘ Check gibt false zurück.

Das bedeutet, dass einige Anweisungen, um ausgeführt zu werden braucht eine Box-T in eine U in einer Art und Weise zu konvertieren, die die Prüfer passieren.

würde ich Ihnen zustimmen, dass die Prüfungsregeln geändert werden sollen, so dass die Codegenerierung Sie wollen tatsächlich überprüfbar ist.

Technisch jedoch ist der Compiler die „richtige“ Sache zu tun auf der Grundlage der ECMA spec.

Sie sollten einen Fehler mit jemandem bei Microsoft einreichen.

Andere Tipps

Diese Einschränkungen seltsam aussehen:

where T : struct, U
where U : class

T ist ein Werttyp, aber in der gleichen Zeit muss von U erben, die ein Referenztyp ist. Ich frage mich, welche Arten könnten die oben genannten Bedingungen erfüllen und ermöglichen es uns, diese Methode zu nennen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top