web-dev-qa-db-fra.com

Casting direct vs 'en tant qu'opérateur?

Considérons le code suivant:

void Handler(object o, EventArgs e)
{
   // I swear o is a string
   string s = (string)o; // 1
   //-OR-
   string s = o as string; // 2
   // -OR-
   string s = o.ToString(); // 3
}

Quelle est la différence entre les trois types de casting (d'accord, le troisième n'est pas un casting, mais vous obtenez l'intention). Lequel devrait être préféré?

665
nullDev
string s = (string)o; // 1

Lance InvalidCastException si o n'est pas un string. Sinon, affecte o à s, même si o est null.

string s = o as string; // 2

Assigne null à s si o n'est pas un string ou si o est null. Pour cette raison, vous ne pouvez pas l'utiliser avec des types de valeur (l'opérateur ne pourrait jamais retourner null dans ce cas). Sinon, affecte o à s.

string s = o.ToString(); // 3

Cause une exception NullReferenceException si o est null. Assigne ce que o.ToString() retourne à s, quel que soit le type o.


Utilisez 1 pour la plupart des conversions - c'est simple et direct. J'ai tendance à ne presque jamais utiliser 2, car si quelque chose ne convient pas, je m'attends généralement à ce qu'une exception se produise. J'ai seulement constaté le besoin de ce type de fonctionnalité return-null avec des bibliothèques mal conçues qui utilisent des codes d'erreur (par exemple, return null = error, au lieu d'utiliser des exceptions).

3 n'est pas un cast et est simplement une invocation de méthode. Utilisez-le lorsque vous avez besoin de la représentation sous forme de chaîne d'un objet non-chaîne.

791
Sander
  1. string s = (string)o; À utiliser quand quelque chose devrait définitivement être l'autre chose.
  2. string s = o as string; Utiliser quand quelque chose pourrait être l'autre chose.
  3. string s = o.ToString(); À utiliser lorsque vous ne vous souciez pas de ce que c'est mais que vous voulez simplement utiliser la représentation sous forme de chaîne disponible.
332
Quibblesome

Cela dépend vraiment de savoir si vous savez si o est une chaîne et ce que vous voulez en faire. Si votre commentaire signifie que o est vraiment une chaîne, je préférerais le transtypage droit (string)o - il est peu probable qu'il échoue.

Le plus gros avantage de l'utilisation de la conversion directe est que lorsque cela échoue, vous obtenez un InvalidCastException , qui vous indique à peu près ce qui ne va pas.

Avec l'opérateur as, si o n'est pas une chaîne, s est défini sur null, ce qui est pratique si vous n'êtes pas sûr et que vous souhaitez tester s:

string s = o as string;
if ( s == null )
{
    // well that's not good!
    gotoPlanB();
}

Toutefois, si vous n'effectuez pas ce test, vous utiliserez s plus tard et une exception NullReferenceException est émise. Celles-ci tendent à être plus courantes et un beaucoup plus difficiles à localiser une fois qu'elles se produisent dans la nature, car presque chaque ligne déréférence une variable et peut en lancer une . Par contre, si vous essayez de transtyper vers un type de valeur (toute primitive ou des structures telles que DateTime ), vous devez utiliser la distribution directe - le as ne fonctionnera pas.

Dans le cas particulier de la conversion en chaîne, chaque objet a un ToString, ainsi votre troisième méthode peut être acceptable si o est null et si vous pensez que la méthode ToString peut faire ce que vous voulez.

29
Blair Conrad

Si vous connaissez déjà le type de conversion, utilisez une conversion de style C:

var o = (string) iKnowThisIsAString; 

Notez que vous ne pouvez effectuer une coercition de type explicite qu’avec une conversion de style C.

Si vous ne savez pas s'il s'agit du type souhaité et si vous l'utiliserez, utilisez comme mot clé :

var s = o as string;
if (s != null) return s.Replace("_","-");

//or for early return:
if (s==null) return;

Notez que as n'appellera aucun opérateur de conversion de type. Il ne sera non nul que si l'objet n'est pas null et natif du type spécifié.

Utilisez ToString () pour obtenir une représentation sous forme de chaîne lisible par l'homme de tout objet, même s'il ne peut pas être converti en chaîne.

9
Mark Cidade

Le mot clé as est correct dans asp.net lorsque vous utilisez la méthode FindControl.

Hyperlink link = this.FindControl("linkid") as Hyperlink;
if (link != null)
{
     ...
}

Cela signifie que vous pouvez utiliser la variable typée plutôt que de devoir la lancer à partir de object comme vous le feriez avec une diffusion directe:

object linkObj = this.FindControl("linkid");
if (link != null)
{
     Hyperlink link = (Hyperlink)linkObj;
}

Ce n'est pas énorme, mais cela économise des lignes de code et des affectations de variables, en plus il est plus lisible

7
Glenn Slaven

'as' est basé sur 'est', mot clé qui vérifie au moment de l'exécution si l'objet est compatible polimorphisme (en gros, si un transt peut être effectué) et renvoie la valeur null en cas d'échec de la vérification.

Ces deux sont équivalents:

Utiliser 'comme':

string s = o as string;

Utiliser 'est':

if(o is string) 
    s = o;
else
    s = null;

Au contraire, la conversion de style c est également effectuée au moment de l'exécution, mais une exception est levée si la conversion ne peut pas être effectuée.

Juste pour ajouter un fait important:

Le mot clé "en tant que" ne fonctionne qu'avec les types de référence. Tu ne peux pas faire:

// I swear i is an int
int number = i as int;

Dans ces cas, vous devez utiliser le casting.

6
Sergio Acosta

2 est utile pour la conversion en un type dérivé.

Supposons que a est un animal:

b = a as Badger;
c = a as Cow;

if (b != null)
   b.EatSnails();
else if (c != null)
   c.EatGrass();

obtiendrez a nourri avec un minimum d'incantations.

5
Joel in Gö

"(chaîne) o" donnera une exception InvalidCastException car il n'y a pas de diffusion directe.

"o as string" aura pour conséquence que s sera une référence nulle et non une exception.

"o.ToString ()" n'est pas une distribution d'une sorte, mais une méthode implémentée par objet, et donc d'une manière ou d'une autre, par chaque classe du .net qui "fait quelque chose" avec l'instance de la classe à laquelle elle est appelée et retourne une chaîne.

N'oubliez pas que pour la conversion en chaîne, il existe également Convert.ToString (someType instanceOfThatType), où someType est l'un des types, essentiellement les types de base des frameworks.

4
Rob

Selon les expériences menées sur cette page: http://www.dotnetguru2.org/sebastienros/index.php/2006/02/24/cast_vs_as

(Cette page a des erreurs de "référence illégale" qui apparaissent parfois, alors actualisez si c'est le cas)

En conclusion, l'opérateur "en tant que" est normalement plus rapide qu'un transtypage. Parfois beaucoup plus rapidement, parfois à peine plus vite.

Je peronsonone chose "comme" est également plus lisible.

Donc, comme il est à la fois plus rapide et "plus sûr" (sans exception), et peut-être plus facile à lire, je recommande d'utiliser "comme" tout le temps.

4
Brady Moritz
string s = o as string; // 2

Est préférable, car il évite la pénalité de performance de la double coulée.

3
Chris S

Toutes les réponses données sont bonnes, si je peux ajouter quelque chose: pour utiliser directement les méthodes et propriétés de string (par exemple, ToLower), vous ne pouvez pas écrire:

(string)o.ToLower(); // won't compile

vous pouvez seulement écrire:

((string)o).ToLower();

mais vous pouvez écrire à la place:

(o as string).ToLower();

L'option as est plus lisible (du moins à mon avis).

3
BornToCode

Il semble que les deux d'entre eux sont conceptuellement différents.

Casting Direct

Les types ne doivent pas être strictement liés. Il vient dans tous les types de saveurs.

  • Casting implicite/explicite personnalisé: Généralement, un nouvel objet est créé.
  • Type de valeur implicite: Copier sans perdre d'informations.
  • Type de valeur explicite: La copie et les informations peuvent être perdues.
  • relation IS-A: Changer le type de référence, sinon lève une exception.
  • Même type: 'Le casting est redondant'.

C'est comme si l'objet allait être transformé en quelque chose d'autre.

opérateur AS

Les types ont une relation directe. Un péché:

  • Types de référence: relation IS-A Les objets sont toujours les mêmes, seule la référence change.
  • Types de valeur: Copier types boxing et nullable.

C'est comme si vous alliez manipuler l'objet d'une manière différente.

échantillons et IL

    class TypeA
    {
        public int value;
    }

    class TypeB
    {
        public int number;

        public static explicit operator TypeB(TypeA v)
        {
            return new TypeB() { number = v.value };
        }
    }

    class TypeC : TypeB { }
    interface IFoo { }
    class TypeD : TypeA, IFoo { }

    void Run()
    {
        TypeA customTypeA = new TypeD() { value = 10 };
        long longValue = long.MaxValue;
        int intValue = int.MaxValue;

        // Casting 
        TypeB typeB = (TypeB)customTypeA; // custom explicit casting -- IL:  call class ConsoleApp1.Program/TypeB ConsoleApp1.Program/TypeB::op_Explicit(class ConsoleApp1.Program/TypeA)
        IFoo foo = (IFoo)customTypeA; // is-a reference -- IL: castclass  ConsoleApp1.Program/IFoo

        int loseValue = (int)longValue; // explicit -- IL: conv.i4
        long dontLose = intValue; // implict -- IL: conv.i8

        // AS 
        int? wraps = intValue as int?; // nullable wrapper -- IL:  call instance void valuetype [System.Runtime]System.Nullable`1<int32>::.ctor(!0)
        object o1 = intValue as object; // box -- IL: box [System.Runtime]System.Int32
        TypeD d1 = customTypeA as TypeD; // reference conversion -- IL: isinst ConsoleApp1.Program/TypeD
        IFoo f1 = customTypeA as IFoo; // reference conversion -- IL: isinst ConsoleApp1.Program/IFoo

        //TypeC d = customTypeA as TypeC; // wouldn't compile
    }
3
Lucas Teixeira

J'aimerais attirer l'attention sur les spécificités suivantes de l'opérateur en tant que :

https://docs.Microsoft.com/en-us/dotnet/csharp/language-reference/keywords/as

Notez que l'opérateur as n'effectue que des conversions de référence, des conversions nullables et des conversions de boxe. L'opérateur as ne peut pas effectuer d'autres conversions, telles que des conversions définies par l'utilisateur, qui devraient plutôt être effectuées à l'aide d'expressions cast.

2
Vadim S.

Utilisez la diffusion directe string s = (string) o; si, dans le contexte logique de votre application, string est le seul type valide. Avec cette approche, vous obtiendrez InvalidCastException et implémenterez le principe de Fail-fast . Votre logique sera protégée contre le dépassement du type non valide ou obtiendra NullReferenceException si elle est utilisée par l'opérateur as.

Si la logique attend plusieurs types différents, transformez string s = o as string; et vérifiez-le sous null ou utilisez l'opérateur is.

Une nouvelle fonctionnalité intéressante est apparue dans C # 7.0 pour simplifier la distribution et la vérification est un correspondance de modèle :

if(o is string s)
{
  // Use string variable s
}

or

switch (o)
{
  case int i:
     // Use int variable i
     break;
  case string s:
     // Use string variable s
     break;
 }
0
Dmitry

Comme personne n'en a parlé, l'instance la plus proche de instanceOf à Java par mot clé est la suivante:

obj.GetType().IsInstanceOfType(otherObj)
0
Bennett Yeo

Lorsque j'essaie d'obtenir la représentation sous forme de chaîne de tout élément (de tout type) pouvant potentiellement être null, je préfère la ligne de code ci-dessous. Il est compact, il appelle ToString () et gère correctement les valeurs NULL. Si o est null, s contiendra String.Empty.

String s = String.Concat(o);
0
xtrem

Les deux formes suivantes de conversion de type (diffusion) sont prises en charge en C #:

|

(C) v

• Convertir le type statique de v en c dans l'expression donnée

• Possible uniquement si le type dynamique de v est c ou un sous-type de c

• Sinon, une exception InvalidCastException est levée.

|

v comme C

• Variante non fatale de (c) v

• Ainsi, convertissez le type statique de v en c dans l'expression donnée

• Retourne la valeur null si le type dynamique de v n'est pas c ou un sous-type de c

0
user4931677