Lors de la programmation des interfaces, j'ai constaté que je fais beaucoup de conversion ou de conversion de type d'objet.
Y a-t-il une différence entre ces deux méthodes de conversion? Si oui, y a-t-il une différence de coût ou comment cela affecte-t-il mon programme?
public interface IMyInterface
{
void AMethod();
}
public class MyClass : IMyInterface
{
public void AMethod()
{
//Do work
}
// Other helper methods....
}
public class Implementation
{
IMyInterface _MyObj;
MyClass _myCls1;
MyClass _myCls2;
public Implementation()
{
_MyObj = new MyClass();
// What is the difference here:
_myCls1 = (MyClass)_MyObj;
_myCls2 = (_MyObj as MyClass);
}
}
De plus, quelle est «en général» la méthode préférée?
Réponses:
La réponse sous la ligne a été écrite en 2008.
C # 7 a introduit la correspondance de motifs, qui a largement remplacé l'
as
opérateur, comme vous pouvez maintenant l'écrire:Notez que
tt
c'est toujours dans la portée après cela, mais pas définitivement attribué. (Il est définitivement attribué dans leif
corps.) C'est légèrement ennuyeux dans certains cas, donc si vous vous souciez vraiment d'introduire le plus petit nombre de variables possible dans chaque étendue, vous voudrez peut-être toujours utiliseris
suivi d'un cast.Je ne pense pas qu'aucune des réponses à ce jour (au moment de commencer cette réponse!) Ait vraiment expliqué où cela valait la peine d'être utilisé.
Ne faites pas ça:
Non seulement cette vérification est effectuée deux fois, mais elle peut également vérifier différentes choses, s'il
randomObject
s'agit d'un champ plutôt que d'une variable locale. Il est possible que le «si» passe mais que la conversion échoue, si un autre thread change la valeurrandomObject
entre les deux.Si
randomObject
vraiment devrait être une instance deTargetType
, c'est-à-dire si ce n'est pas le cas, cela signifie qu'il y a un bug, alors le casting est la bonne solution. Cela lève immédiatement une exception, ce qui signifie qu'aucun travail supplémentaire n'est effectué sous des hypothèses incorrectes, et l'exception affiche correctement le type de bogue.Si
randomObject
peut être une instance deTargetType
etTargetType
est un type de référence, utilisez un code comme celui-ci:Si
randomObject
peut être une instance deTargetType
etTargetType
est un type valeur, nous ne pouvons pas l'utiliseras
avecTargetType
lui-même, mais nous pouvons utiliser un type nullable:(Remarque: actuellement, c'est en fait plus lent que + cast . Je pense que c'est plus élégant et cohérent, mais c'est parti.)
Si vous n'avez vraiment pas besoin de la valeur convertie, mais que vous avez juste besoin de savoir s'il s'agit d' une instance de TargetType, alors l'
is
opérateur est votre ami. Dans ce cas, peu importe que TargetType soit un type de référence ou un type de valeur.Il peut y avoir d'autres cas impliquant des génériques où cela
is
est utile (parce que vous ne savez pas si T est un type de référence ou non, vous ne pouvez donc pas l'utiliser comme) mais ils sont relativement obscurs.Je l'ai presque certainement utilisé
is
pour le cas de type valeur avant maintenant, sans avoir pensé à utiliser un type nullable etas
ensemble :)EDIT: Notez qu'aucun des éléments ci-dessus ne parle de performances, à l'exception du cas du type de valeur, où j'ai remarqué que la décompression vers un type de valeur nullable est en fait plus lente - mais cohérente.
Selon la réponse de naasking, is-and-cast ou is-and-as sont à la fois aussi rapides que la vérification as-and-null avec les JIT modernes, comme le montre le code ci-dessous:
Sur mon ordinateur portable, ils s'exécutent tous en 60 ms environ. Deux choses à noter:
Alors ne nous inquiétons pas des performances. Soucions-nous de l'exactitude et de la cohérence.
Je maintiens que is-and-cast (ou is-and-as) ne sont pas sûrs lorsqu'ils traitent avec des variables, car le type de la valeur à laquelle il se réfère peut changer en raison d'un autre thread entre le test et le cast. Ce serait une situation assez rare - mais je préfère avoir une convention que je peux utiliser de manière cohérente.
Je maintiens également que la vérification as-then-null donne une meilleure séparation des préoccupations. Nous avons une instruction qui tente une conversion, puis une instruction qui utilise le résultat. Le is-and-cast ou is-and-as effectue un test, puis une autre tentative de conversion de la valeur.
Autrement dit, quelqu'un pourrait-il jamais écrire:
C'est en quelque sorte ce que fait-et-cast - bien que de manière évidemment moins chère.
la source
if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}
ou utiliserswitch
/case
voir les documents"as" renverra NULL s'il n'est pas possible de transtyper.
lancer avant lèvera une exception.
Pour les performances, lever une exception est généralement plus coûteux en temps.
la source
Voici une autre réponse, avec une comparaison IL. Considérez la classe:
Regardez maintenant l'IL produit par chaque méthode. Même si les codes op ne signifient rien pour vous, vous pouvez voir une différence majeure - isinst est appelé suivi de castclass dans la méthode DirectCast. Donc, deux appels au lieu d'un essentiellement.
Le mot-clé isinst contre la classe de distribution
Ce billet de blog a une comparaison décente entre les deux façons de le faire. Son résumé est:
Personnellement, j'utilise toujours As, car il est facile à lire et recommandé par l'équipe de développement .NET (ou Jeffrey Richter de toute façon)
la source
L'une des différences les plus subtiles entre les deux est que le mot clé "as" ne peut pas être utilisé pour le cast lorsqu'un opérateur de cast est impliqué:
Cela ne se compilera pas (bien que je pense que ce soit le cas dans les versions précédentes) sur la dernière ligne car les mots clés "as" ne prennent pas en compte les opérateurs de cast. La ligne
string cast = (string)f;
fonctionne très bien cependant.la source
as ne lève jamais d'exception s'il ne peut pas effectuer la conversion en retournant null à la place ( comme pour les types de référence uniquement). Donc, utiliser as est fondamentalement équivalent à
Les conversions de style C, en revanche, lèvent une exception lorsqu'aucune conversion n'est possible.
la source
Pas vraiment une réponse à votre question, mais ce que je pense est un point connexe important.
Si vous programmez sur une interface, vous ne devriez pas avoir besoin de caster. Espérons que ces lancers soient très rares. Sinon, vous devrez probablement repenser certaines de vos interfaces.
la source
Veuillez ignorer les conseils de Jon Skeet, concernant: éviter le modèle de test et de distribution, c'est-à-dire:
L'idée que cela coûte plus cher qu'un casting et un test nul est un MYTHE :
C'est une micro-optimisation qui ne fonctionne pas. J'ai effectué de vrais tests , et test-and-cast est en fait plus rapide que cast-and-null-comparaison, et c'est aussi plus sûr parce que vous n'avez pas la possibilité d'avoir une référence nulle dans la portée en dehors du si le cast échouer.
Si vous voulez une raison pour laquelle le test et la diffusion sont plus rapides, ou du moins pas plus lents, il y a une raison simple et complexe.
Simple: même les compilateurs naïfs fusionneront deux opérations similaires, comme test-and-cast, en un seul test et une seule branche. cast-and-null-test peut forcer deux tests et une branche, une pour le test de type et la conversion en null en cas d'échec, une pour la vérification null elle-même. À tout le moins, ils seront tous deux optimisés en un seul test et une seule branche, de sorte que test-and-cast ne serait ni plus lent ni plus rapide que cast-and-null-test.
Complexe: pourquoi le test et le cast sont plus rapides: cast-and-null-test introduit une autre variable dans la portée externe que le compilateur doit suivre pour la vivacité, et il peut ne pas être en mesure d'optimiser cette variable en fonction de la complexité de votre contrôle - le flux est. Inversement, test-and-cast n'introduit une nouvelle variable que dans une portée délimitée afin que le compilateur sache que la variable est morte après la fin de la portée et peut ainsi optimiser l'allocation des registres.
Alors s'il vous plait, VEUILLEZ laisser ce DIE "conseil cast-and-null-test est meilleur que test-and-cast". S'IL VOUS PLAÎT. test-and-cast est à la fois plus sûr et plus rapide.
la source
ref
paramètre. Il est sûr pour les variables locales, mais pas pour les champs. Je serais intéressé à exécuter vos benchmarks, mais le code que vous avez donné dans votre article de blog n'est pas complet. Je suis d'accord pour ne pas micro-optimiser, mais je ne pense pas que l'utilisation de la valeur deux fois soit plus lisible ou élégante que l'utilisation de "as" et d'un test de nullité. (J'utiliserais certainement un casting droit plutôt que "as" après un is, btw.)Si la conversion échoue, le mot clé 'as' ne lève pas d'exception; il définit la variable sur null (ou sur sa valeur par défaut pour les types de valeur) à la place.
la source
Ce n'est pas une réponse à la question mais un commentaire sur l'exemple de code de la question:
Habituellement, vous ne devriez pas avoir à convertir un objet, par exemple IMyInterface, en MyClass. La grande chose au sujet des interfaces est que si vous prenez un objet en entrée qui implémente une interface, vous n'avez pas à vous soucier du type d'objet que vous obtenez.
Si vous convertissez IMyInterface en MyClass, vous supposez déjà que vous obtenez un objet de type MyClass et cela n'a aucun sens d'utiliser IMyInterface, car si vous alimentez votre code avec d'autres classes qui implémentent IMyInterface, cela casserait votre code ...
Maintenant, mon conseil: si vos interfaces sont bien conçues, vous pouvez éviter beaucoup de transtypage.
la source
L'
as
opérateur ne peut être utilisé que sur les types de référence, il ne peut pas être surchargé et il reviendranull
si l'opération échoue. Il ne lèvera jamais d'exception.Le casting peut être utilisé sur tous les types compatibles, il peut être surchargé et il lèvera une exception si l'opération échoue.
Le choix de l'utilisation dépend des circonstances. Il s'agit principalement de savoir si vous souhaitez lever une exception en cas d'échec de la conversion.
la source
Ma réponse ne concerne que la vitesse dans les cas où nous ne vérifions pas le type et nous ne vérifions pas les valeurs nulles après le casting. J'ai ajouté deux tests supplémentaires au code de Jon Skeet:
Résultat:
N'essayez pas de vous concentrer sur la vitesse (comme je l'ai fait) car tout cela est très très rapide.
la source
as
conversion (sans vérification d'erreur) fonctionnait environ 1 à 3% plus rapidement que la conversion (environ 540 ms contre 550 ms sur 100 millions d'itérations). Aucun ne fera ou ne cassera votre candidature.En plus de tout ce qui a déjà été exposé ici, je suis juste tombé sur une différence pratique, je pense, à noter, entre un casting explicite
par rapport à l'utilisation de l'
as
opérateur.Voici l'exemple:
Conclusion : GenericCaster2 ne fonctionnera pas avec les types de structure. GenericCaster le fera.
la source
Si vous utilisez les PIA Office ciblant le .NET Framework 4.X, vous devez utiliser le mot clé as , sinon il ne sera pas compilé.
La diffusion est OK lorsque vous ciblez .NET 2.0:
Lors du ciblage de .NET 4.X, les erreurs sont les suivantes:
erreur CS0656: Le compilateur manquant a requis le membre «Microsoft.CSharp.RuntimeBinder.Binder.Convert»
erreur CS0656: Le compilateur manquant a requis le membre «Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create»
la source
Le
as
mot-clé fonctionne de la même manière qu'une conversion explicite entre les types de référence compatibles, à la différence près qu'il ne déclenche pas d'exception si la conversion échoue. Au contraire, il donne une valeur nulle dans la variable cible. Étant donné que les exceptions sont très coûteuses en termes de performances, elles sont considérées comme une bien meilleure méthode de casting.la source
Ce que vous choisissez dépend fortement de ce qui est requis. Je préfère le casting explicite
parce que si l'objet doit être de type IMyInterface et qu'il ne l'est pas - c'est définitivement un problème. Il est préférable d'obtenir l'erreur le plus tôt possible car l'erreur exacte sera corrigée au lieu de corriger son effet secondaire.
Mais si vous traitez des méthodes qui acceptent
object
comme paramètre, vous devez vérifier son type exact avant d'exécuter un code. Dans ce cas, ilas
serait utile que vous puissiez éviterInvalidCastException
.la source
Cela dépend, voulez-vous vérifier null après avoir utilisé "as" ou préféreriez-vous que votre application lève une exception?
Ma règle d'or est que si je m'attends toujours à ce que la variable soit du type auquel je m'attends au moment où je veux, j'utilise un transtypage. S'il est possible que la variable ne soit pas convertie en ce que je veux et que je suis prêt à gérer les valeurs nulles à l'aide de as, j'utiliserai as.
la source
Jetez un œil à ces liens:
ils vous montrent quelques détails et tests de performances.
la source
Le problème de l'OP est limité à une situation de casting spécifique. Le titre couvre bien plus de situations.
Voici un aperçu de toutes les situations de casting pertinentes auxquelles je peux actuellement penser:
la source