Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Atteint de JavaScriptite Aiguë [Cyril Durand]

Expert ASP.net Ajax et WCF, Cyril Durand parle dans son blog de point techniques sur ASP.net, ASP.net Ajax, JavaScript, WCF et .net en général. Cyril est également consultant indépendant, n'hésitez pas à le contacter pour de l'assistance sur vos projets

Actualités

  • Blog de Cyril DURAND, passionné de JavaScript, Ajax, ASP.net et tout ce qui touche au developpement Web Client-Side.

    N'hésitez pas à me contacter pour vos projets .net : architecture, accompagnement, formation, ...

    View Cyril Durand's profile on LinkedIn
    hit counters


    Expertise Commerce server et BizTalk

[C#] Cast ou as – opérateur de conversion classique ou utilisation du mot clé as

Récemment, on m’a demandé s’il valait mieux utiliser l’operateur de conversion classique ou l’operateur as pour effectuer une conversion. J’ai donc cherché à savoir quelle était la meilleure méthode au niveau du temps d’exécution.

Les méthodes de conversion classique et l’opérateur as ne sont pas exactement équivalents. Bien sur, tous deux permettent de changer le type de l’objet, mais certaines différences existent.

Si l’on essaye de convertir un objet en un mauvais type, avec as, il n’y aura pas d’erreur, l’opération retournera null alors qu’avec un cast classique une exception de type InvalidCastException sera levée. De plus, l’opérateur as  n’exécutera pas les operateurs de conversions explicites.

Afin de comparer les performances, voici le code que j’ai utilisé, j’ai tout d’abord créé 2 classes avec une relation d’héritage entre elles, puis j’effectue une boucle de 100 millions de cast. J’ai choisi d’écrire le code du test directement dans la méthode plutôt qu’une méthode externe afin d’éviter les optimisations d’inlining du compilateur et du JIT.

public class Person { public void PersonPouet() { } } public class Developper : Person { public void DevelopperPouet() { } }
static void CaseX() { Console.Write("Case#X\t"); Stopwatch watcher = new Stopwatch(); Person p = new Developper(); watcher.Reset(); watcher.Start(); for (int i = 0; i < 100000000; i++) { // code test case } watcher.Stop(); Console.Write("\t{0:N0}", watcher.ElapsedMilliseconds); watcher.Reset(); watcher.Start(); for (int i = 0; i < 100000000; i++) { // code test as } watcher.Stop(); Console.Write("\t{0:N0}", watcher.ElapsedMilliseconds); Console.Write("\r\n"); }

J’ai ensuite testé différents cas d’utilisation des opérateurs de conversion :

Cas 1 : Conversion seule sans utiliser l’objet retourné

// cast Developper d = (Developper)p; // as Developper d = p as Developper;

Cas 2 : Conversion et vérification la non nullité de d

// cast Developper d = (Developper)p; // as Developper d = p as Developper; if (d != null) { }

Cas 3 : Utilisation classique sans la vérification que p est bien un Developper

// cast Developper d = (Developper)p; d.DevelopperPouet(); // as /// attention : on ne teste pas que d est différent de null /// si p n'est pas un Developper alors on obtiendra une NullReferenceException Developper d = p as Developper; d.DevelopperPouet();

Cas 4 : Utilisation classique avec la vérification sur p

// cast Developper d = (Developper)p; d.DevelopperPouet(); // as Developper d = p as Developper; if (d != null) { d.DevelopperPouet(); }

Cas 5 : Conversion classique et utilisation d’une méthode de la classe de base, le cast est ici inutile.

// cast Developper d = (Developper)p; d.PersonPouet(); // as Developper d = p as Developper; d.PersonPouet();

Cas 6 : Vérification du type avant l’opération de conversion

// cast if (p is Developper) { Developper d = (Developper)p; d.DevelopperPouet(); } // as Developper d = p as Developper; if (d != null) { d.DevelopperPouet(); }

Cas 7 : Vérification du type avant l’opération de conversion avec une conversion impossible

// cast if (p is String) { String s = (String)p; s.Trim(); } // as String s = p as String; if (s != null) { s.Trim(); }

Cas 8 : Utilisation de GetType() pour vérifier le type de l’objet.

Note : Attention les 2 cas ne sont pas exactement équivalents, si p est une classe enfant de Developper alors le premier cas ne se réalisera, alors que le deuxième si.

// cast if (p.GetType() == typeof(Developper)) { Developper d = (Developper)p; d.DevelopperPouet(); } // as Developper d = p as Developper; if (d != null) { d.DevelopperPouet(); }

Résultats

Voici les résultats pour 100 millions d’exécutions exprimées en millisecondes. J’ai d’abord effectué mes tests en compilant pour la plate-forme x86 avec la CLR2 (.net 3.5 SP1) et la CLR 4 (.net 4.0).

 image 

Puis en compilant pour la plate-forme x64.

image

Explications

Pour le cas 1, grâce aux optimisations du compilateur, le mot clé as est plus rapide. Dans ce cas, le compilo constate que le résultat de la conversion n’est jamais utilisé, il ne compile alors pas le corps de la méthode.

Pour les cas 2 à 5, on observe que l’opération de cast est légèrement plus rapide. On voit également que sous .net 4, le cast a été fortement amélioré : +65% de gain de temps.

Pour les cas 6 et 7, on voit que le mot clé is ralentit les différentes méthodes.

Enfin, pour le cas 8, l’appel de la méthode GetType() est plus rapide que l’utilisation de l’opérateur as. Je ne sais pas exactement d’ou cela vient, il faudrait étudier de plus près la compilation JIT.

Conclusion

Si vous êtes sûr du type de l’objet à convertir, il est alors préférable d’utiliser un cast. Si par contre vous n’êtes pas sur du type de l’objet et que vous avez besoin de vérifier le type, il est alors préférable d’utiliser l’operateur as.

Ces tests ont également montré les différences entre la CLR x64 et x86, on a pu voir que sous la CLR2 la version x86 était plus optimisée que la version x64, cela n’est plus le cas sous la CLR4. On voit également que la CLR4 est globalement plus optimisé que la CLR2, notamment en x64.

Malgré les différences en termes de performances, il ne faut pas oublier que les différences s’expriment en nanosecondes …

Posted: mardi 20 avril 2010 15:03 par cyril
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

KooKiz a dit :

Résultats sans surprise : le mot clé "as" ajoutant une vérification supplémentaire, son exécution ne pouvait être que plus lente. Par contre il pourrait être intéressant de pousser un peu plus loin les tests en comparant l'IL généré :)

# avril 20, 2010 15:38

cyril a dit :

J'ai regardé le MSIL, à part pour le cas1, il n'y a rien de très interessant. C'est juste une translation du C#, le compilo ne fait pas d'optimisation au niveau du MSIL.

# avril 20, 2010 21:36

FREMYCOMPANY a dit :

Les équivalents VB.NET, c'est quoi ?

Parce que sous VB.NET, on a trois opérateurs de conversions, pas deux.

CType [Developper d = (Developper)p]

TryCast

DirectCast

Par déduction, je dirais que TryCast est du même type que "as", mais DirectCast semble différent des deux autres. Ca n'a donc pas d'équivalent C# ?

# avril 20, 2010 22:05

Jem a dit :

Pour moi DirectCast = Cast : C'est une conversion de type qui ne marche que si le type est bien le même.

La spécificité VB.NET ce serait plutôt le CType qui applique en plus des règles de conversions implicites.

Par contre je pense que ca ne s'applique qu'aux types de base :

Dim d As Object = 1.2D 'Il faut déclarer en Object et pas en Decimal parce que sinon ca ne compile même pas

Dim s1 As String = CType(d, String) ' Ca marche s1 = "1,2"

Dim s2 As String = TryCast(d, String) 'Ca renvoie Nothing

Dim s3 As String = DirectCast(d, String) 'Ca renvoie une System.InvalidCastException

Il y a d'ailleurs pas mal de subtilités en VB.NET sur les régles de conversion implicites qui sont utilisées lors de l'utilisation du CType

# avril 21, 2010 09:31

Jem a dit :

Une des subtilités les plus surprenantes étant l'utilisation de "l'arrondi du banquier", c'est à dire :

Dim i1 = CType(3.5D,Integer) 'i1=4

Dim i2 = CType(4.5D,Integer) 'i2=4

Vers les 2/3 de la page, il y a des détails sur les différents opérateurs de conversion en VB :

http://msdn.microsoft.com/en-us/library/Aa289509#vbtchmicrosoftvisualbasicnetinternalsanchor5

# avril 21, 2010 09:48

warning a dit :

Constatations intéressante surtout pour le CLR4 sous x64.

Par contre pour le cas n°8 pourquoi ne pas avoir essayé avec l'opérateur 'is' ainsi:

if (p is Developper)

plutôt que

if (p.GetType() == typeof(Developper))

?

# octobre 25, 2010 11:42

cyril a dit :

@warning : C'est le cas N°7 non ?

# octobre 25, 2010 11:53

smo a dit :

as et is correspondent à la même instruction au niveau IL. Voir une intéressante discussion ici à ce propos: http://blogs.msdn.com/b/ericlippert/archive/2010/10/11/debunking-another-myth-about-value-types.aspx

PS: "p.GetType() == typeof(Developer)" n'est pas équivalent à "p is Developer". L'équivalent c'est "typeof(Developer).IsAssignableFrom(p.GetType())".

# novembre 3, 2010 09:08
Les commentaires anonymes sont désactivés

Les 10 derniers blogs postés

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01

- SharePoint Online: Script PowerShell pour supprimer une colonne dans tous les sites d’une collection par Blog Technique de Romelard Fabrice le 11-27-2018, 18:01