Question

Examinons le code MSIL généré pour la méthode générique suivante:

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

Rechercher:

.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
}

Mais pour le code générique ci-dessus, l'IL représentation plus efficace devrait être:

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

Il est connu des contraintes que la valeur est barricadés type de référence . Unbox.any opcode est totalement redondant, car après box opcode la valeur à pile IL sera déjà une référence valable pour !!U, qui peut être utilisé sans aucune unboxing.

Pourquoi le compilateur C # 3.0 ne pas utiliser les métadonnées de contraintes pour émettre un code générique plus efficace? Unbox.any donne une petite tête (juste 4x 5x plus lent), mais pourquoi ne pas émettre un meilleur code dans ce scénario?

Était-ce utile?

La solution

Il semble que le compilateur fait cela à cause de quelques problèmes avec le vérificateur.

L'IL que vous voulez le compilateur de générer ne sont pas vérifiables, et donc le compilateur C # ne peut pas générer (tout le code C # en dehors des contextes « dangereux » doivent être vérifiables).

Les règles de la "compatibilité du type de vérification" sont données à la section 1.8.1.2.3, Partion III de la spécification ECMA.

On dit qu'un type 'S' est compatible avec la vérification d'un type 'T' ou (S: = T) en utilisant les règles suivantes:

  1. [: = est réflexif] Pour tous les types de vérification S, S: S =
  2. [: = est transitive] Pour tous les types de vérification S, T et U si S: = T et T: = U, alors S:. = U
  3. S:. = T si S est la classe de base de T ou d'une interface mise en oeuvre par T et T ne sont pas un type de valeur
  4. objet:. = T si T est un type d'interface
  5. S: T = si S et T sont les deux interfaces et la mise en œuvre de T nécessite la mise en œuvre S
  6. S: = null si S est un type d'objet ou d'une interface
  7. S []: T = [], si S: = T et les matrices sont soit les deux vecteurs (de base zéro, le rang ni une) ou est un vecteur et les deux ont le même rang. (Cette règle traite de covariance de tableau.)
  8. Si S et T sont des pointeurs de méthode, alors S: = T si les signatures (types de retour, les types de paramètres et convention d'appel) sont les mêmes.

De ces règles, le seul qui pourrait être applicable dans ce cas, est classé n ° 3.

Cependant, # 3 ne correspond pas à votre code, car « U » est pas une classe de base de « T », et ce n'est pas une interface de base de « T », de sorte que le « ou » chèque retourne false.

Cela signifie que certaines instructions doit être exécuté afin de convertir un T en U d'une manière qui passera boxed le vérificateur.

Je suis d'accord avec vous que les règles de vérification doivent être modifiées, de sorte que la génération du code que vous voulez est effectivement vérifiable.

Techniquement, cependant, le compilateur fait la chose basé sur la spécification ECMA « correcte ».

Vous devez déposer un bug avec quelqu'un chez Microsoft.

Autres conseils

Ces contraintes ne paraissent pas:

where T : struct, U
where U : class

T est un type de valeur, mais dans le même temps doit hériter de U qui est un type de référence. Je me demande quels types pourraient satisfaire les contraintes ci-dessus et nous permettent d'appeler cette méthode.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top