51 votes

L'équivalent en C# de DirectCast en VB.NET

Le C# a-t-il un équivalent de DirectCast de VB.NET ?

Je suis conscient qu'il y a des castings () et le mot-clé 'as', mais ceux-ci s'alignent sur CType et TryCast.

Pour être clair, ces mots-clés font ce qui suit ;

Les casts CType/() : S'il s'agit déjà du bon type, faites-le couler, sinon cherchez un convertisseur de type et invoquez-le. Si aucun convertisseur de type n'est trouvé, une InvalidCastException est levée.

Mot clé TryCast/"as : S'il s'agit du bon type, le cast, sinon retourner null.

DirectCast : S'il s'agit du bon type, faites-le, sinon lancez une InvalidCastException.

Après avoir expliqué ce qui précède, certaines personnes ont encore répondu que () est équivalent, je vais donc expliquer plus en détail pourquoi ce n'est pas vrai.

DirectCast ne permet que des conversions de type "narrowing" ou "widening" sur l'arbre d'héritage. Elle ne supporte pas les conversions à travers différentes branches comme le fait (), c'est-à-dire :

C# - cela compile et fonctionne :

//This code uses a type converter to go across an inheritance tree
double d = 10;
int i = (int)d;

VB.NET - cela ne COMPILE PAS

'Direct cast can only go up or down a branch, never across to a different one.
Dim d As Double = 10
Dim i As Integer = DirectCast(d, Integer)

L'équivalent en VB.NET de mon code C# est CType :

'This compiles and runs
Dim d As Double = 10
Dim i As Integer = CType(d, Integer)

11 votes

+1 pour définir les mots-clés et ne pas obliger les gens à les chercher.

4 votes

string s = "10"; int i = (int)s; ne compile pas en C#.

0 votes

Avez-vous essayé d'utiliser reflector pour comparer le MSIL ?

14voto

Greg Bogumil Points 1288

Il semble clair que la fonctionnalité que vous voulez n'est pas en C#. Essayez ceci cependant...

static T DirectCast<T>(object o, Type type) where T : class
{
    if (!(type.IsInstanceOfType(o)))
    {
        throw new ArgumentException();
    }
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}

Ou, même si c'est différent du VB, appelez-le comme :

static T DirectCast<T>(object o) where T : class
{
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}

0 votes

Est en accord avec ce que je pensais à l'origine. Merci.

2 votes

Vous pouvez retirer le T : class si vous utilisez is au lieu de as . static T DirectCast<T>(object o) { if(o != null && o is T) return (T)o; else throw new InvalidCastException(); }

3 votes

Dans votre code, vous n'utilisez pas le paramètre "type", vous pouvez donc le laisser de côté. Ou l'utiliser dans une vérification supplémentaire : if (typeof(T) != type) throw new InvalidArgumentException(...) ;.

8voto

Dan Tao Points 60518

DEUXIÈME MISE À JOUR:

OK, voici une méthode C# qui a été proposé pour soit disant faire essentiellement ce qu' DirectCast n'en VB.NET.

static T DirectCast<T>(object o) where T : class
{
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}

Voici les problèmes avec la méthode ci-dessus:

  1. Il a un where T : class contrainte, qui DirectCast ne le sont pas.
  2. Il les boîtes de son argument comme un System.Object -- encore une fois, pas vrai d' DirectCast (au moins pas que je sache).
  3. Il utilise as inutilement (c'est pourquoi il a l' class contrainte en premier lieu); appelant (T)o va jeter un InvalidCastException si cela ne fonctionne pas; pourquoi vérifier si la valeur correspond à l'aide de as, qu'à jeter de la même exception qui a été levée si vous aviez fait l' (T)o route pour commencer?

La méthode pourrait vraiment être remanié afin de fournir les mêmes résultats que l' DirectCast comme suit:

static T DirectCast<T>(object o) {
    return (T)o;
}

Drôle d'observation: vraiment tout ce que cette méthode est en train de faire est la boxe une valeur, puis tentez d'unbox. En d'autres termes, DirectCast<int>(12.0) serait vraiment le même que (int)(object)12.0 (et non plus jeter une exception). La réalisation de ce fait le projet de DirectCast<T> méthode assez inutile, tout à fait.

Maintenant, voici un exemple de la façon dont DirectCast et casting avec () sont "différents" entre VB.NET et C#:

VB:

Dim i As Integer = 12
Dim l As Long = DirectCast(i, Long) ' does not compile '

C#:

int i = 12;
long l = i; // DOES compile

OK, donc on compile, l'autre n'a pas. Mais regardez ce code. Quel est le point de DirectCast lorsque vous connaissez déjà un type de l'objet? Ce n'est pas une comparaison réaliste, parce que dans VB.NET il n'y aurait jamais aucune raison d'appeler à l' DirectCast comme le code ci-dessus ne. (Si vous vouliez convertir une valeur connu pour être de type System.Int32 pour une valeur de type System.Int64 dans VB.NET, vous pouvez utiliser CLng, pas DirectCast.) Si il y avait une variable typée comme System.Object y, alors il serait logique d'utiliser DirectCast, et le code ci-dessous serait en effet équivalent:

VB:

Dim i As Integer = 12
Dim o As Object = i
Dim l As Long = DirectCast(o, Long) ' compiles, throws an exception '

C#:

int i = 12;
object o = i;
long l = (long)o; // compiles, throws an exception

Donc je maintiens qu' DirectCast dans VB.NET, quel que soit le scénario dans lequel il fait réellement sens pour l'utiliser (c'est à dire, lorsque le type d'un objet n'est pas connu au moment de la compilation), est la même que la droite ()-style fonte en C#.


EDIT: eh Bien, honte sur moi pour poster quelques VB code ne compile pas. Après reconsidérer ce que je disais, je retire ma deuxième réponse, mais de maintenir la première.

Si vous faites allusion à l'utilisation de l' DirectCast où vous prenez un objet de type inconnu et d'essayer de convertir le type désiré, puis c' est la même qu'en C#'() cast:

VB:

Dim o As Object = SomeObject()
Dim i As Integer = DirectCast(o, Integer)

C#:

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

C'est parce que, si o est tapé comme un System.Object, puis l' () fonctionnement en C# va tenter de unbox. Ceci ne fonctionnera pas si les types ne correspondent pas exactement; par exemple, si o est un coffret System.Double, alors (int)o lèvera une exception, car o doit être "unboxed" en tant que System.Double avant d'être converti en System.Int32 (si vous ne me croyez pas, essayez-la vous-même!).


Remarque: le dessous est inexacte parce DirectCast ne pas effectuer l'élargissement des conversions; en tout cas, je vais la laisser pour la postérité.

D'autre part, lorsque l'on traite avec l'élargissement plutôt que de le restreindre les conversions, à l'aide de l' () fonctionnement en C# n'a plus de travail que de simplement la coulée, comme vous l'avez indiqué (c'est à dire, vous pouvez le faire (int)someDouble). Dans ce scénario, DirectCast est équivalent à la plaine de vieux affectation en C#:

VB:

Dim i As Integer = 12
Dim l As Long = DirectCast(i, Long) ' does not compile, actually '

C#:

int i = 12;
long l = i;

0 votes

Votre code VB ne se compile pas. Je suppose qu'au lieu de 12 comme paramètre de type pour directcast, vous vouliez dire long, mais cela ne compile pas non plus. Assurez-vous également que l'option strict est activée, sinon vous allez avoir une tonne de conversions implicites (le compilateur VB aime lire dans les pensées).

0 votes

@csauve : Bien vu. C'était stupide de ma part ! Dans tous les cas, jetez un coup d'oeil à mon premier point. Je crois vraiment que DirectCast est en réalité la même que celle de C# () lorsqu'elle est utilisée sur des objets typés comme System.Object .

0 votes

@csauve : Au fait, concernant l'option stricte... dans le code que j'ai posté à l'origine, l'option Infer fonctionne tout aussi bien. (J'ai quand même ajouté les types, par souci d'exhaustivité).

1voto

Michael Buen Points 20453

En fait, le compilateur ne fait que détecter la violation de DirectCast s'il en déduit que la variable typée ne peut pas être convertie dans l'autre type.

Ce sont les équivalents réels :

double d = 10;
int i = (int)d;

Dim d As Double = 10
Dim i As Integer = d

Notez la dangerosité de cette construction. Si vous vous contentez d'assigner un double à un entier dans VB.NET, le double sera accidentellement réduit à un entier.

Alors que les programmeurs C# bénéficient de la sécurité au moment de la compilation de ne pas réduire accidentellement la taille d'une variable en .NET. Les programmeurs VB.NET doivent se contenter de toujours utiliser DirectCast comme une habitude de programmation sûre.

Ce sont les équivalents réels :

// Will not compile, cannot convert double to int

double d = 10;
int i = d;

' Will not compile, cannot convert double to int

Dim d As Double = 10
Dim i As Integer = DirectCast(d, Integer)

Concernant Commentaire de Dan Tao :

Il n'est pas nécessaire d'utiliser DirectCast en C#. Le runtime empêche également le chargement d'une valeur longue en valeur entière. C'est ce que l'OP prétend, que C# n'a pas DirectCast, que DirectCast peut empêcher l'assignation de différents types de variables, alors que "parce que" C# n'a pas ce DirectCast, il se trompe silencieusement lors de l'assignation de différents types. Mais comme vous pouvez le voir, ce n'est pas le cas. Le casting de C# est exactement la même chose que DirectCast. Cela provoquera un InvalidCastException erreur d'exécution :

long l = 10;
object o = l;
int i = (int)o;

Cela provoquera également le même erreur d'exécution comme ci-dessus :

Dim l As Long = 10
Dim o As Object = l
Dim i As Integer = DirectCast(o, Integer)

C'est là que la partie "amusante" entre en jeu. Avec VB.NET, vous devez vous souvenir de nombreux mots-clés pour accomplir quelque chose. En C#, si un mot-clé donné peut être utilisé dans un autre scénario (comme ici le downcasting de variable), ils n'inventeront pas un autre mot-clé juste pour le faire.

En C#, il suffit de faire cela :

long l = 10;
object o = l;
int i = (int)(long)o;

En VB.NET, si vous voulez vraiment déclasser la variable, et que vous voulez le faire de manière orthogonale, c'est-à-dire en vous souvenant d'un seul mot-clé, vous devez faire ceci :

Dim l As Long = 10
Dim o As Object = l
Dim i As Integer = DirectCast(DirectCast(o, Long), Integer)

Mais cela ne compilera pas, alors comment pouvons-nous réaliser le downcasting de long en integer ? Vous devez vous souvenir des autres mots-clés de VB.NET. Alors qu'en C#, c'est orthogonal, vous décomposez une variable en utilisant cette construction (typehere) vous pouvez aussi faire du downcast/upcast en utilisant la même construction. (typehere) . En VB.NET, il y a une déconnexion fondamentale entre le chargement d'une valeur à partir d'un objet et son downcasting. Donc, en VB.NET, vous devez faire ceci :

Dim l As Long = 10
Dim o As Object = l
Dim i As Integer = CType(o, Integer)

Hmm Je pense que la confusion de l'OP provient de l'utilisation multiple en C# de la fonction (typehere) . Premièrement, il est utilisé pour le downcasting ; deuxièmement, la même construction (voir la première partie de ce post, object o = l ) est également utilisé pour l'unboxing de la valeur de l'objet, qui, rassurez-vous, a le comportement de conversion de type sûr de DirectCast. Ils sont les mêmes !

Cette baisse de régime...

long l = 1;
int i = (int) l;

...n'est pas équivalent à :

Dim l As Long = 1
Dim i As Integer = DirectCast(l, Integer)

Si vous voulez faire du downcasting, vous devez faire ça :

Dim l As Long = 1
Dim i As Integer = CInt(l) ' Can also use CType

Maintenant, si un programmeur VB.NET programme par intention, et ne s'endort pas en codant, pourquoi utilisera-t-il DirectCast alors qu'il sait parfaitement qu'il ne peut pas assigner des types différents ? Si le programmeur VB.NET voulait vraiment faire du downcast, il n'aurait pas dû essayer DirectCast en premier lieu. Maintenant, le programmeur VB.NET, en découvrant que DirectCast ne peut pas être utilisé pour le downcasting, doit revenir en arrière et remplacer ce qu'il a écrit par CInt (ou CType).

1 votes

Essayez d'activer l'option stricte -- j'espère que la plupart des magasins obligent les développeurs vb à écrire avec cette option activée.

0 votes

@Michael : C'est ce que je pensais aussi ; mais comme csauve l'a fait remarquer dans un commentaire à ma réponse, les exemples de code à la fin de votre réponse ne sont en fait pas des équivalents. A long peut être affecté à un int en C#, mais VB.NET n'autorisera pas une Long à affecter à un Integer en utilisant DirectCast .

0 votes

@Dan Tao - Voir mon commentaire ci-dessus.

1voto

Eamon Nerbonne Points 21663

Vous pouvez le mettre en œuvre vous-même :

static T CastTo<T>(this object obj) { return (T)obj; }

Utilisez-le comme suit :

3.5.CastTo<int>(); //throws InvalidCastException.

Cela fonctionne et n'implique pas de convertisseurs définis par l'utilisateur, car les génériques sont "résolus" au moment de l'exécution, alors que les conversions de type sont résolues au moment de la compilation - le cadre ne génère pas réellement des implémentations distinctes pour chaque type de générique. T mais partage plutôt l'implémentation pour des T Le runtime ne dispose donc pas des informations nécessaires pour résoudre les conversions personnalisées.

0voto

Kevin Points 57797

Il existe deux types de cast en C#. Sans code supplémentaire, il n'existe pas d'équivalent au mot-clé DirectCast en C#. Ce qui s'en rapproche le plus sans le créer vous-même est l'utilisation de () .

Vous avez :

My_Object c = (My_Object)object

et

My_Object c = object as My_Object

Dans le premier cas, si la distribution échoue, une erreur est lancée. Vous dites, "Je sais ce qu'est cet objet, et si ce n'est pas le cas, il y a un problème."

Dans la seconde, c se voit attribuer la valeur null lorsque cela est possible (null ne peut pas être attribué aux types de valeurs). Dans ce cas, vous dites "Je pense savoir ce que c'est, mais si ce n'est pas le cas, n'envoyez pas d'erreur, car il se peut que tout aille bien".

Autre poste expliquant le casting :

Quelle est la différence entre les castings de type explicite et implicite ?

0 votes

Oops j'ai mélangé ma terminologie. Merci

1 votes

Comme indiqué ci-dessus, les castings () permettent les convertisseurs de type, DirectCast ne le fait pas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X