Dans les blogs de développement, les exemples de code en ligne et (récemment) même un livre, je n'arrête pas de trébucher sur un code comme celui-ci:
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 conversion directe: (T)x
. Si vous n'êtes pas sûr, vous pouvez utiliser as
mais vous devez vérifier null
avant d'effectuer une opération. Le code ci-dessus ne fait que transformer un (utile) InvalidCastException
en un (inutile) NullReferenceException
.
Suis-je le seul à penser qu'il s'agit d'un abus flagrant du mot clé as
? Ou ai-je oublié quelque chose d'évident et le modèle ci-dessus a-t-il un sens?
Votre compréhension est vraie. Cela ressemble à essayer de micro-optimiser pour moi. Vous devez utiliser une distribution normale lorsque vous êtes sûr du type. En plus de générer une exception plus sensible, elle échoue également rapidement. Si vous vous trompez au sujet de votre hypothèse sur le type, votre programme échouera immédiatement et vous pourrez voir immédiatement la cause de l'échec plutôt que d'attendre un NullReferenceException
ou ArgumentNullException
ou même une erreur logique dans le futur. En général, une expression as
qui n'est pas suivie d'un contrôle null
est une odeur de code.
D'un autre côté, si vous n'êtes pas sûr du cast et que vous vous attendez à ce qu'il échoue, vous devriez utiliser as
au lieu d'un cast normal entouré d'un try-catch
bloquer. De plus, l'utilisation de as
est recommandée par rapport à une vérification de type suivie d'un transtypage. Au lieu de:
if (x is SomeType)
((SomeType)x).SomeMethod();
qui génère une instruction isinst
pour le mot clé is
, et une instruction castclass
pour la conversion (effectuant effectivement la jeté deux fois), vous devez utiliser:
var v = x as SomeType;
if (v != null)
v.SomeMethod();
Cela ne génère qu'une instruction isinst
. L'ancienne méthode présente un risque potentiel dans les applications multithreads, car une condition de concurrence critique pourrait amener la variable à changer de type après le contrôle de is
et l'échec de la chaîne de distribution. La dernière méthode n'est pas sujette à cette erreur.
La solution suivante est non recommandée pour une utilisation dans le code de production. Si vous détestez vraiment une construction aussi fondamentale en C #, vous pouvez envisager de passer à VB ou à un autre langage).
Si on déteste désespérément la syntaxe du casting, il/elle peut écrire une méthode d'extension pour imiter le casting:
public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
return (T)o;
}
et utilisez une syntaxe soignée [?]:
obj.To<SomeType>().SomeMethod()
IMHO, as
prend tout son sens lorsqu'il est combiné avec un contrôle null
:
var y = x as T;
if (y != null)
y.SomeMethod();
L'utilisation de "comme" ne s'applique pas aux conversions définies par l'utilisateur, alors que la distribution les utilisera le cas échéant. Cela peut être une différence importante dans certains cas.
J'ai écrit un peu à ce sujet ici:
Je comprends votre point. Et je suis d’accord avec l’idée maîtresse: un opérateur de casting communique "Je suis sûr que cet objet peut être converti en ce type, et je suis disposé à risquer une exception si je me trompe", alors qu’un opérateur "en" communique "Je ne suis pas sûr que cet objet puisse être converti en ce type; donnez-moi une valeur nulle si je me trompe".
Cependant, il y a une différence subtile. (x comme T) .W Whatever () communique "Je sais que x ne peut pas seulement être converti en T, mais que cela implique uniquement des conversions de référence ou unboxing, et que x n'est pas nul". Cela communique des informations différentes de celles de ((T) x) .W Whatever (), et c’est peut-être ce que l’intention de l’auteur du code.
J'ai souvent vu des références à cet article trompeur comme preuve que "en tant que" est plus rapide qu'un casting.
L'un des aspects trompeurs les plus évidents de cet article est le graphique, qui n'indique pas ce qui est mesuré: je soupçonne que sa mesure a échoué jette (où " comme "est évidemment beaucoup plus rapide car aucune exception n’est levée).
Si vous prenez le temps de faire les mesures, alors vous verrez que le casting est, comme on peut s'y attendre, plus rapide que "comme" lorsque le casting réussit.
Je soupçonne que cela pourrait être l’une des raisons du "culte de la cargaison": utiliser le mot-clé en tant que mot clé au lieu d’un casting.
La diffusion directe nécessite une paire de parenthèses plus que le mot clé as
. Ainsi, même dans le cas où vous êtes à 100% sûr du type, cela réduit l'encombrement visuel.
Convenu sur la chose d'exception, cependant. Mais au moins pour moi, la plupart des utilisations de as
se résument à vérifier après null
, ce que je trouve plus agréable que d’attraper une exception.
C'est simplement parce que les gens aiment son apparence, 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é. Je préférerais que C # adopte la syntaxe Javascript suivante:
object o = 1;
int i = int(o);
Ou définissez un opérateur to
, l’équivalent de as
:
object o = 1;
int i = o to int;
99% du temps quand j'utilise "comme", c'est quand je ne suis pas sûr du type d'objet réel
var x = obj as T;
if(x != null){
//x was type T!
}
et je ne veux pas attraper les exceptions de casting explicites ni faire un casting deux fois, en utilisant "est":
//I don't like this
if(obj is T){
var x = (T)obj;
}
Les gens aiment tellement as
que cela les met à l’abri des exceptions ... Comme une garantie sur une boîte. Un gars met une garantie de fantaisie sur la boîte parce qu'il veut que vous vous sentiez au chaud et au chaud à l'intérieur. Vous pensez que vous mettez cette petite boîte sous votre oreiller la nuit, la fée garantie pourrait descendre et laisser un quart, est-ce que j'ai raison Ted?
Retour sur le sujet ... lors de l'utilisation d'une diffusion directe, il existe la possibilité pour une exception de distribution non valide. Donc, les gens appliquent as
comme solution globale à tous leurs besoins en termes de casting parce que as
(en lui-même) ne lèvera jamais une exception. Mais la chose amusante à ce sujet est dans l'exemple que vous avez donné (x as T).SomeMethod();
vous échangez une exception de distribution non valide pour une exception de référence null. Ce qui brouille le vrai problème quand on voit l'exception.
En général, je n'utilise pas trop as
. Je préfère le test is
car il me semble plus lisible et plus logique que d'essayer un cast et de rechercher la valeur null.
Ce doit être l'un de mes top peeves .
Le D & E de Stroustrup et/ou un article de blog que je ne trouve pas en ce moment aborde la notion d'opérateur to
qui répondrait au point soulevé par https://stackoverflow.com/users/73070/johannes- rossel (c'est-à-dire, même syntaxe que as
mais avec DirectCast
sémantique).
La raison pour laquelle cela n’a pas été mis en œuvre est qu’un casting devrait causer de la douleur et être laid, de sorte que l’on l’éloigne de l’utiliser.
Dommage que des programmeurs "intelligents" (souvent des auteurs de livres (Juval Lowy IIRC)) évitent cela en abusant de as
de cette manière (le C++ ne propose pas de as
, probablement pour cette raison).
Même VB a plus de cohérence dans le fait d'avoir une syntaxe uniforme qui vous oblige à choisir un TryCast
ou DirectCast
et composent votre esprit !
Je pense que le mot-clé as
pourrait être considéré comme une version plus élégante du thème dynamic_cast
depuis C++.
C'est probablement plus populaire sans raison technique mais simplement parce que c'est plus facile à lire et plus intuitif. (Ne pas dire que ça le rend préférable d'essayer juste de répondre à la question)
Une des raisons d'utiliser "en tant que":
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
}