web-dev-qa-db-fra.com

C # équivaut à DirectCast de VB.NET?

Est-ce que C # a un équivalent au DirectCast de VB.NET?

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

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

CType/() convertit : si le type est déjà correct, convertissez-le, sinon recherchez un convertisseur de type et appelez-le. Si aucun convertisseur de type n'est trouvé, lève une exception InvalidCastException.

TryCast/"as" mot-clé : s'il s'agit du type correct, lancez-le, sinon retournez null.

DirectCast : Si le type est correct, lancez-le, sinon émettez une InvalidCastException.

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

DirectCast permet uniquement de réduire ou d'élargir les conversions sur l'arbre d'héritage. Il ne prend pas en charge les conversions entre différentes branches, comme (),

C # - cela compile et exécute:

//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 dans VB.NET de mon code C # est CType:

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

Il semble clair que la fonctionnalité que vous souhaitez n’est pas en C #. Essayez ceci si ...

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 bien, même s’il est différent du VB, appelez-le comme suit:

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

SECOND UPDATE:

OK, voici une méthode C # qui a été proposée pour faire prétendument ce que DirectCast fait dans 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 une contrainte where T : class, ce que DirectCast ne possède pas.
  2. Il encadre son argument en tant que System.Object - encore une fois, ce n'est pas vrai de DirectCast (du moins, pas que je sache).
  3. Il utilise inutilement as (c'est pourquoi il a la contrainte class en premier lieu); appeler (T) o lancera un InvalidCastException s'il ne fonctionne pas; pourquoi vérifier si la valeur correspond à l'aide de as, uniquement pour émettre la même exception que celle qui aurait été émise si vous aviez choisi la route (T)o?

La méthode pourrait vraiment être réécrite pour fournir les mêmes résultats que DirectCast comme suit:

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

Observation amusante: en réalité, toute cette méthode consiste à boxer une valeur puis à tenter de la déballer. En d'autres termes, DirectCast<int>(12.0) serait vraiment identique à (int)(object)12.0 (et l'un ou l'autre lirait une exception). En réalisant cela, la méthode DirectCast<T> proposée est plutôt inutile.

Maintenant, voici un exemple de la façon dont DirectCast et le transtypage 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 l'un compile, l'autre pas. Mais regardez ce code. Quel est l'intérêt de DirectCast quand vous connaissez déjà le type d'un objet? Ce n'est pas une comparaison réaliste, car dans VB.NET, il n'y aurait jamais de raison d'appeler DirectCast comme le code ci-dessus fait. (Si vous souhaitez convertir une valeur de type System.Int32 en une valeur de type System.Int64 dans VB.NET , vous utiliseriez CLng, pas DirectCast.) S'il y avait une variable typée System.Object, -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

Je maintiens donc que DirectCast dans VB.NET, quel que soit le scénario dans lequel il est judicieux de l'utiliser (c'est-à-dire lorsque le type d'un objet n'est pas connu à la compilation), est identique en tant que type droit ()- transtypé en C #.


EDIT: Eh bien, honte à moi de poster du code VB qui n'a pas été compilé. Après avoir reconsidéré ce que je disais, je retire ma seconde réponse mais je maintiens la première.

Si vous faites référence à l'utilisation de DirectCast où vous prenez un objet de type inconnu et essayez de le convertir en type souhaité, alors est identique à la distribution de C # ():

VB:

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

C #:

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

En effet, si o est saisi sous la forme d'un System.Object, l'opération () en C # tentera de le décompresser. Cela échouera si les types ne correspondent pas exactement; par exemple, si o est un System.Double en boîte, alors (int)o lève une exception car odoit doit être déballé en tant que System.Double avant de pouvoir le convertir en System.Int32 (si vous ne me croyez pas, essayez-le vous-même!).


Remarque: les informations ci-dessous sont inexactes car DirectCast ne fait pas effectuer des conversions avec élargissement; en tout cas, je le laisse à la postérité.

D'autre part, lorsque vous utilisez des conversions d'élargissement ou de rétrécissement, l'utilisation de l'opération () en C # fait plus de travail que la simple conversion, comme vous l'avez indiqué (c'est-à-dire que vous pouvez utiliser (int)someDouble). Dans ce scénario, DirectCast équivaut à une ancienne affectation simple 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;
8
Dan Tao

En fait, le compilateur attrape simplement la violation DirectCast s'il en déduit que la variable typée ne peut pas être convertie en un 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, lorsque vous affectez simplement 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é lors de la compilation, ils ne réduisent pas accidentellement la variable .NET. Les programmeurs VB.NET doivent toujours faire de DirectCast 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) 

[MODIFIER]

@ Dan Tao:

Il n’est pas nécessaire d’utiliser DirectCast en C #, l’exécution empêche également le chargement de valeurs longues à entières. C'est ce que prétend csauve, que C # n'a pas DirectCast, que DirectCast peut empêcher l'attribution de différents types de variables, alors que "parce que" C # n'a pas ce DirectCast, il se trompera silencieusement en assignant différents types. Mais comme vous pouvez le constater, ce n’est pas le cas, le casting de C # est exactement identique à DirectCast. Cela provoquera InvalidCastException erreur d'exécution:

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

Cela provoquera également la même erreur runtime que ci-dessus:

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

Maintenant, c’est là que la partie "amusante" entre en jeu. Avec VB.NET, vous devez vous rappeler 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 dans celui-ci, la modification d'une variable dénaturée), ils n'inventeront pas un autre mot-clé uniquement pour y arriver.

En C #, il suffit de faire ceci:

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

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

 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 parvenir à un downcasting long en entier? Vous devez vous rappeler les autres mots clés de VB.NET. Tandis qu'en C #, il est orthogonal, vous décochez la variable en utilisant cette construction (typehere), vous décédez également/en utilisant la même construction (typehere). Dans VB.NET, il existe une déconnexion fondamentale entre le chargement d’une valeur à partir d’un objet et son décroissance. Donc, dans 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 csauve provient de l'utilisation multiple de (typehere) par C #, tout d'abord, il est utilisé pour le downcasting; deuxièmement, la même construction (voir la première partie de cet article, object o = l) est également utilisée pour le décodage de la valeur d'un objet, ce qui garantit que le comportement de conversion de type sécurisé de DirectCast est identique;

Cette décadence ...

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 effectuer un downcasting, procédez comme suit:

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 n’est pas en sommeil pendant le codage, pourquoi utilisera-t-il DirectCast alors qu’il est pleinement conscient du fait qu’il ne peut pas affecter différents types? Si ce que le programmeur de VB.NET souhaitait réellement, c’est de le décaisser, il ne devrait pas tenter de DirectCast en premier lieu. Maintenant, le programmeur VB.NET, après avoir découvert que DirectCast ne peut pas être utilisé pour le downcasting, doit effacer ce qu'il a écrit et le remplacer par CInt (ou CType)

1
Michael Buen

Vous pouvez l'implémenter vous-même:

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

Utilisable 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 mais les conversions de types sont résolues au moment de la compilation - le cadre ne génère pas en réalité d'implémentations distinctes pour chaque T, mais partage plutôt l'implémentation T similaire, de sorte que le moteur d'exécution ne dispose pas des informations nécessaires pour résoudre les conversions personnalisées.

1
Eamon Nerbonne

DirectCast et () ne génèrent pas toujours le même IL, je pense donc qu'il s'agit d'une différence entre les compilateurs VB et C #.

AFAICT, les types de référence sont convertis à l'aide de l'instruction castclass IL, tandis que pour les types de valeur, le compilateur génère le IL correspondant en fonction des types d'entrée.

En C #, le passage de double à integer émet l’instruction conv.i4 IL, qui écrasera joyeusement le bit de signe ou quoi que ce soit dans la sortie, si la valeur est trop grande. En VB, c'est une erreur de compilation.

Fait intéressant, si vous utilisez une variable object intermédiaire pour contenir le double, la conversion échouera pour C # et VB ... mais au moment de l'exécution. Les deux compilateurs émettent une instruction unbox au lieu d'essayer de convertir.

0
SSS

Je pense que ce scénario résume bien pourquoi DirectCast a une fausse impression de sécurité de vérification du type à la compilation pour le type non-objet (mot-clé d'objet), et est simplement destiné à être en arrière-plan.

float f = 10;
long l = f;

Option Strict On    
Dim f As Single = 10
Dim l As Long = f

Un codeur C #, après avoir découvert que float n'est pas assignable directement à long et ne compilera pas, fera ceci:

long l = (long)f;

Qui est correct.

Maintenant, passons à notre codeur VB.NET, après avoir découvert que le flottant n’est pas assignable longtemps et qu’il ne compilera pas, tentera ceci:

Dim l As Long = DirectCast(f, Long)

Quelques secondes plus tard ...

Programmeur de VB.Net: "S'il vous plaît, laissez-moi faire ce que je vous demande, compilez s'il vous plaît, s'il vous plaît ... !!!"

Après quelques instants de navigation sur Googling-fu et MSDN:

Programmeur VB.NET: "Ah .. je dois donc utiliser cette construction CLng ou CType pour la conversion de variables"

Dim l As Long = CLng(f)

C’est ce que je voulais dire par DirectCast a une fausse idée de la sécurité de la vérification du type à la compilation. DirectCast est simplement destiné à être mis en arrière si un programmeur ne sait pas quand et où il devrait être utilisé. DirectCast est une couverture de sécurité qui est pas portée tout le temps.

Quelle est l'utilité de DirectCast dans ce scénario s'il ne sera pas utilisé après tout?


[MODIFIER]

@Jules

Je ne prétends pas que tous les programmeurs de VB.NET ne savent pas quelle est la véritable utilisation de DirectCast; certains d'entre eux savent vraiment que DirectCast est uniquement destiné à être utilisé pour des types d'objet (et des types primitifs encadrés dans un objet) uniquement .

Un scénario dans lequel un codeur VB.NET recodant du code C # existant en VB.NET aboutira à une conclusion erronée est celui de la symétrie des langages attendus (que ce soit correct ou non).

Quand il voit dans le code cette construction ...

TextBox txt = (TextBox)sender;

... Il traduira cela en ceci:

Dim txt As TextBox = DirectCast(sender, TextBox)

Qui est correct.

Maintenant, parce que nous, les programmeurs, aimons la symétrie, certains d’entre nous (je le serais peut-être aussi si je ne connaissais pas CLng) auront tendance à convertir ce code ...

/* numbers are stored in file as float(component's file structure 
is designed by 3rd party company) */
float f = file.ReadFloat(0); 
long l = (long)f; // but we don't care about using the fractional part

...pour ça:

Dim f As Single = file.ReadFloat(0)
Dim l As Long = DirectCast(f, Long)

Si une personne C # est celle qui convertit le code C # en VB.NET, il sera frustré par le manque apparent de symétrie ici.

Mais pour une personne de VB.NET chargée de convertir le code C # en VB.NET, il aura l’impression que le compilateur C # ne détecte pas les affectations de types incompatibles, alors que VB.NET le détecte. Maintenant, pour cette apparente découverte, vanterons cette fonctionnalité de VB.NET auprès de ses collègues et de certains forums.

Mais de peur que le programmeur VB.NET commette l'erreur d'inférer à tort l'intention du premier code. Le fragment de code du C # ci-dessus a commencé sa vie comme ça a été initialement écrit comme ceci:

float f = file.ReadFloat(0); 
long l = f; 

Et cela ne compilera pas, le compilateur C # attrape des assignations de types incompatibles, de la même manière que l'équivalent VB.NET avec Option Strict On ne compilera pas non plus que (même si ne compilera pas lorsque Option Strict est défini sur On, trop indulgent) . Nous devons donc transtyper float to long en utilisant (long). Devient ceci: long l = (long)f;

Maintenant, pour convertir un type de variable en un autre type compatible, dans la même veine que nous convertissons ce code ...

TextBox txt = (TextBox)sender;

... à ce code:

Dim txt As TextBox = DirectCast(sender, Textbox)

Nous devons convertir ce code ...

long l = (long)f; // will compile

... à ce code:

Dim l As Long = DirectCast(f, Long) ' will not compile

Mais hélas, cela ne compilera pas, lors du transtypage entre types primitifs compatibles, c’est là que DirectCast se démarque. Il n’offre aucune symétrie par rapport au code C # ci-dessus, il ne peut pas être utilisé pour le transtypage de types primitifs compatibles, malgré son nom DirectJeter.

À mon sens, DirectCast devrait s'appeler CastObject, car il ne peut transtyper qu'entre types d'objet (et également types primitifs encadrés dans objet) de toute façon. DirectCast n’a vraiment aucun intérêt à attribuer des types primitifs compatibles (entier, double et leurs contreparties inférieure et supérieure). Lorsque vous affectez des types de primitifs compatibles, DirectCast cesse d’être utile, surtout si vous le reculez de toute façon et que vous le remplacez par celui qui convient.

À l’inverse, je pense que la construction DirectCast doit être modifiée pour permettre la conversion de types compatibles, comme le font les anciens et les nouveaux langages depuis, par exemple. C, C++, C #, Java, Delphi, D, etc. Ce faisant, il offrira une symétrie significative à VB.NET par rapport aux autres langages en matière de transtypage. En faisant cela, nous pouvons aussi jeter (hypothétiquement seulement, nous ne pouvons pas faire échouer d’autres programmes qui reposent sur d’anciennes fonctions) à toute une pléthore de fonctions dont les noms ne correspondent pas directement à ses types (par exemple CInt, CDbl, CSng, etc.). utilisera simplement DirectCast au lieu d’eux.

0
Michael Buen

Avez-vous vraiment essayé d'exécuter votre exemple de code?

En ce qui concerne...

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

... Vous avez postulé que ça va courir. Il ne court pas non plus

0
Michael Buen

Laissez-moi essayer de tenter le coup.

Tout d'abord, laissez-moi être clair sur ce point. This NE SERA PAS compiler:

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

CType de VB

En VB, vous utiliseriez:

Dim s as String = "10"
Dim i as Integer = CType(s, Integer)

En C #, j'utiliserais:

string s = "10";
int i = Convert.ToInt32(s);

DirectCast de VB

Si c'est le bon type, lancez-le, sinon jetez un InvalidCastException.

La distribution directe ne peut que monter ou descendre un branche, jamais à un autre un.

À partir de cette explication, ce serait un équivalent direct de la distribution C #. Cependant, en C #, il vous suffirait de spécifier l'opérateur de conversion uniquement pour la réduction. Le lancement est totalement facultatif. Exemple:

// casting down
object t = "some random string needing to be casted down";
string s = (string) t;
// casting up
object a = s;
// explicitly casting up although it's totally optional
object b = (object) s;

C # cast ne cherche pas de convertisseur de type. Il ne cherchera que toute surcharge d'opérateur explicite/implicite définie pour le type que vous essayez de convertir.


TryCast de VB

Vous avez déjà bien compris que cela équivaut à C # en tant que mot clé.

0
Amry

Vous avez deux types de casting en C #. Sans code supplémentaire, il n'y a pas d'équivalent au mot clé DirectCast en C #. Le plus proche de vous sans créer vous-même est d'utiliser ().

Tu as:

My_Object c = (My_Object)object

et

My_Object c = object as My_Object

Dans le premier cas, si le casting échoue, une erreur est renvoyée. Vous dites: "Je sais ce que cet objet est, et si ce n'est pas le cas, il y a quelque chose qui cloche."

Dans la seconde, c se voit attribuer la valeur null si possible (la valeur null ne peut pas être affectée aux types de valeur). Dans celui-ci, vous dites "Je pense que je sais ce que c'est, mais sinon, ne jetez pas d'erreur, car rien ne peut être faux."

Autre post expliquant le casting:

Quelle est la différence entre les conversions de types explicites et implicites?

0
kemiller2002