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)
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;
}
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:
where T : class
, ce que DirectCast
ne possède pas.System.Object
- encore une fois, ce n'est pas vrai de DirectCast
(du moins, pas que je sache).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 o
doit 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;
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)
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.
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.
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.
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
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;
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);
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.
Vous avez déjà bien compris que cela équivaut à C # en tant que mot clé.
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?