Je ne pense pas que les réponses jusqu'à présent (à l'époque, à partir de cette réponse!) ont vraiment expliquer en quoi c'est utile.
-
Ne pas faire ceci:
// Bad code - checks type twice for no reason
if (randomObject is TargetType)
{
TargetType foo = (TargetType) randomObject;
// Do something with foo
}
Ce n'est pas seulement de vérifier deux fois, mais il est peut-être la vérification de choses différentes, si randomObject
est un domaine plutôt qu'une variable locale. Il est possible que le "si" à passer, mais ensuite le casting à l'échec, si un autre thread modifie la valeur de randomObject
entre les deux.
-
Si randomObject
vraiment devrait être une instance d' TargetType
, c'est à dire si elle ne l'est pas, cela signifie qu'il ya un bug, le casting, c'est la bonne solution. Qui lève une exception immédiatement, ce qui signifie que le travail est fait sous des hypothèses erronées, et à l'exception affiche correctement le type de bug.
// This will throw an exception if randomObject is non-null and
// refers to an object of an incompatible type. The cast is
// the best code if that's the behaviour you want.
TargetType convertedRandomObject = (TargetType) randomObject;
-
Si randomObject
pourrait être une instance d' TargetType
et TargetType
est un type de référence, puis utiliser le code comme ceci:
TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject
}
-
Si randomObject
pourrait être une instance d' TargetType
et TargetType
est un type valeur, alors nous ne pouvons pas utiliser as
avec TargetType
lui-même, mais nous pouvons utiliser un type nullable:
TargetType? convertedRandomObject = randomObject as TargetType?;
if (convertedRandomObject != null)
{
// Do stuff with convertedRandomObject.Value
}
(Remarque: actuellement, c'est en fait plus lentement que est de + en fonte. Je pense que c'est plus élégant et cohérent, mais il nous continuons.)
Si vous n'avez pas vraiment besoin de la valeur convertie, mais vous avez juste besoin de savoir si c' est une instance de TargetType, puis l' is
- opérateur est votre ami. Dans ce cas, il n'a pas d'importance si TargetType est un type de référence ou une valeur de type.
Il peut y avoir d'autres cas impliquant des médicaments génériques où is
est utile (parce que vous ne pouvez pas savoir si T est un type référence ou pas, de sorte que vous ne peut pas utiliser), mais ils sont relativement obscure.
J'ai presque certainement utilisé is
de la valeur type de cas, jusqu'à maintenant, n'ayant pas pensé à utiliser un type nullable et as
ensemble :)
EDIT: à Noter qu'aucun des ci-dessus parle de la performance autre que la valeur de type de cas, où j'ai noté que unboxing à nullable type de valeur est en fait plus lente, mais constante.
Comme par naasking réponse, est-et de la fonte ou est-et-les aussi vite que comme-et-null-vérifier moderne, avec des équipes communes d'enquête, comme le montre le code ci-dessous:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 30000000;
static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i + 1] = "x";
values[i + 2] = new object();
}
FindLengthWithIsAndCast(values);
FindLengthWithIsAndAs(values);
FindLengthWithAsAndNullCheck(values);
}
static void FindLengthWithIsAndCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = (string) o;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and Cast: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithIsAndAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = o as string;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and As: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithAsAndNullCheck(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
string a = o as string;
if (a != null)
{
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("As and null check: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
}
Sur mon portable, tout exécuter à propos de 60ms. Deux choses à noter:
- Il n'y a pas de différence significative entre eux. (En fait, il existe des situations dans lesquelles la plus-null-vérifier certainement est plus lent. Le code ci-dessus fait la vérification de type facile parce que c'est pour une classe scellée; si vous êtes de vérifier qu'une interface, la balance penche légèrement en faveur des plus-null-case.)
- Ils sont tous incroyablement rapide. Ce tout simplement ne va pas être le goulot d'étranglement dans votre code, sauf si vous avez vraiment ne vont pas faire quelque chose avec les valeurs par la suite.
Donc, nous allons vous inquiétez pas au sujet de la performance. Nous allons vous soucier de l'exactitude et de la cohérence.
Je maintiens, c'est-à-cast (ou est-et-as) sont à la fois dangereux lorsqu'ils traitent avec des variables, comme le type de la valeur qu'il désigne peut changer en raison d'un autre thread entre le test et la fonte. Qui serait assez rare de la situation - mais je préfère avoir une convention qui je peux utiliser de manière cohérente.
J'ai aussi maintenir que l'as-puis-null-vérifier donne une meilleure séparation des préoccupations. Nous avons une déclaration qui tente une conversion, puis une instruction qui utilise le résultat. L'est-et de la fonte ou est-et comme on l'effectue un test et puis une autre tentative pour convertir la valeur.
Pour le dire d'une autre façon, quelqu'un aurait-il jamais écrire:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
C'est en quelque sorte de ce qui est-et-cast - bien que, de toute évidence, plutôt moins cher.