Est-il judicieux d’utiliser «as» au lieu d’une dissortingbution même s’il n’y a pas de vérification nulle?

Dans les blogs de développement, les exemples de code en ligne et (même) récemment un livre, je continue à trébucher sur le code comme ceci:

var y = x as T; y.SomeMethod(); 

ou pire encore:

 (x as T).SomeMethod(); 

Cela n’a pas de sens pour moi. Si vous êtes sûr que x est de type T , vous devez utiliser une dissortingbution directe: (T)x . En cas de doute, vous pouvez utiliser as mais vous devez vérifier la valeur null avant d’effectuer certaines opérations. Tout ce que fait le code ci-dessus est de transformer une InvalidCastException (utile) en une InvalidCastException (inutile).

Suis-je le seul à penser que c’est un abus flagrant du mot-clé? Ou ai-je manqué quelque chose d’évident et le motif ci-dessus a-t-il un sens?

Votre compréhension est vraie. Cela ressemble à essayer de micro-optimiser pour moi. Vous devez utiliser une dissortingbution normale lorsque vous êtes certain du type. En plus de générer une exception plus sensible, elle échoue aussi rapidement. Si vous avez tort sur votre hypothèse concernant le type, votre programme échouera immédiatement et vous pourrez immédiatement voir la cause de l’échec au lieu d’attendre une NullReferenceException ou ArgumentNullException ou même une erreur logique dans le futur. En général, une expression as qui n’est pas suivie par une vérification null quelque part est une odeur de code.

D’un autre côté, si vous n’êtes pas sûr de la dissortingbution et que vous vous attendez à ce qu’elle échoue, vous devez utiliser as une dissortingbution normale un bloc try-catch . De plus, l’utilisation de as recommandé est recommandée lors d’une vérification de type suivie d’une dissortingbution. Au lieu de:

 if (x is SomeType) ((SomeType)x).SomeMethod(); 

qui génère une instruction isinst pour le mot is clé is et une instruction castclass pour le cast (exécutant effectivement le cast deux fois), vous devez utiliser:

 var v = x as SomeType; if (v != null) v.SomeMethod(); 

Cela génère uniquement une instruction isinst . La première méthode présente une faille potentielle dans les applications multithread car une condition de concurrence peut entraîner le changement de type de la variable après la réussite de la vérification is et l’échec de la ligne de conversion. Cette dernière méthode n’est pas sujette à cette erreur.


La solution suivante n’est pas recommandée pour une utilisation dans le code de production. Si vous détestez vraiment une construction aussi fondamentale en C #, vous pourriez envisager de passer à VB ou à un autre langage.

Dans le cas où l’on déteste désespérément la syntaxe de la dissortingbution, il / elle peut écrire une méthode d’extension pour imiter la dissortingbution:

 public static T To(this object o) { // Name it as you like: As, Cast, To, ... return (T)o; } 

et utiliser une syntaxe soignée [?]:

 obj.To().SomeMethod() 

À mon humble avis, as sens lorsque combiné avec une vérification null :

 var y = x as T; if (y != null) y.SomeMethod(); 

L’utilisation de ‘as’ n’applique pas les conversions définies par l’utilisateur alors que le cast les utilisera, le cas échéant. Cela peut être une différence importante dans certains cas.

J’ai écrit un peu à ce sujet ici:

http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

Je comprends votre point Et je suis d’accord avec l’idée: un opérateur de diffusion communique “Je suis sûr que cet object peut être converti en ce type, et je suis prêt à risquer une exception si je me trompe”, alors qu’un opérateur “as” communique “Je ne suis pas sûr que cet object puisse être converti en ce type; donnez-moi une valeur null si je me trompe”.

Cependant, il y a une différence subtile. (x comme T) .Whatever () communique “Je sais non seulement que x peut être converti en T, mais que cela implique uniquement des conversions de référence ou de débogage, et que x n’est pas nul”. Cela communique des informations différentes de ((T) x) .Whatever (), et c’est peut-être ce que l’auteur du code a l’intention de faire.

J’ai souvent vu des références à cet article trompeur comme preuve que “comme” est plus rapide que le casting.

L’un des aspects les plus évidents de cet article est le graphique, qui n’indique pas ce qui est mesuré: je soupçonne qu’il mesure les lancers échoués (où “as” est évidemment beaucoup plus rapide car aucune exception n’est levée).

Si vous prenez le temps de faire les mesures, vous verrez que le casting est, comme on peut s’y attendre, plus rapide que “as” lorsque la dissortingbution réussit.

Je soupçonne que cela peut être une des raisons de l’utilisation du mot-clé “culte cargaison” plutôt que d’une dissortingbution.

La dissortingbution directe nécessite une paire de parenthèses plus que le mot-clé as . Donc, même dans le cas où vous êtes sûr à 100% du type, cela réduit l’encombrement visuel.

D’accord sur la chose d’exception, cependant. Mais au moins pour moi, la plupart des utilisations se résument à vérifier par la suite null , ce que je trouve plus agréable que d’attraper une exception.

99% du temps lorsque j’utilise “as”, c’est quand je ne suis pas sûr du type d’object réel

 var x = obj as T; if(x != null){ //x was type T! } 

et je ne veux pas intercepter des exceptions de cast explicite ni faire deux fois un cast, en utilisant “is”:

 //I don't like this if(obj is T){ var x = (T)obj; } 

C’est juste parce que les gens aiment la façon dont ça a l’air, c’est très lisible.

Regardons les choses en face: l’opérateur de casting / conversion dans les langages de type C est assez terrible, en termes de lisibilité. J’aimerais mieux que C # adopte la syntaxe Javascript de:

 object o = 1; int i = int(o); 

Ou définir un opérateur to , l’équivalent de casting de:

 object o = 1; int i = o to int; 

Les gens aiment autant parce que cela les met à l’abri des exceptions… Comme la garantie d’une boîte. Un gars met une garantie de luxe sur la boîte, car il veut que vous sentiez tout chaud et grillé à l’intérieur. Vous pensez que vous mettez cette petite boîte sous votre oreiller la nuit, la fée de garantie pourrait descendre et partir un quart, j’ai raison Ted?

De retour sur le sujet … lorsqu’on utilise une dissortingbution directe, il existe une possibilité pour une exception de dissortingbution invalide. Les gens postulent donc as solution globale à tous leurs besoins de casting, car ils ne jetteront jamais une exception. Mais la chose la plus amusante à ce sujet, c’est dans l’exemple que vous avez donné (x as T).SomeMethod(); vous échangez une exception de conversion non valide pour une exception de référence null. Ce qui masque le vrai problème lorsque vous voyez l’exception.

Je n’utilise généralement pas trop. Je préfère le test is parce que pour moi, il semble plus lisible et plus logique que d’essayer une dissortingbution et de vérifier la nullité.

Ce doit être l’un de mes meilleurs peeves .

Stroustrup’s D & E et / ou un article de blog que je ne trouve pas en ce moment traite de la notion d’opérateur to qui aborderait le point soulevé par https://stackoverflow.com/users/73070/johannes-rossel (c’est-à-dire la même syntaxe que avec la sémantique DirectCast ).

La raison pour laquelle cela n’a pas été implémenté est qu’un cast devrait causer de la douleur et être moche pour que vous puissiez vous en passer.

Dommage que les programmeurs «intelligents» (souvent auteurs de livres (Juval Lowy IIRC)) contournent ce problème en abusant as cela (C ++ n’offre pas un as , probablement pour cette raison).

Même VB a plus de cohérence en ayant une syntaxe uniforme qui vous oblige à choisir un TryCast ou un DirectCast et à vous décider !

Je crois que le mot-clé as pourrait être considéré comme une version plus élégante du dynamic_cast de C ++.

C’est probablement plus populaire sans raison technique, mais simplement parce qu’il est plus facile à lire et plus intuitif. (Ne pas dire que cela rend mieux juste essayer de répondre à la question)

Une raison d’utiliser “comme”:

 T t = obj as T; //some other thread changes obj to another type... if (t != null) action(t); //still works 

Au lieu de (mauvais code):

 if (obj is T) { //bang, some other thread changes obj to another type... action((T)obj); //InvalidCastException }