C#'s equivalent to VB.NET's DirectCast
Asked Answered
C

11

53

Does C# have an equivalent to VB.NET's DirectCast?

I am aware that it has () casts and the 'as' keyword, but those line up to CType and TryCast.

To be clear, these keywords do the following;

CType/() casts: If it is already the correct type, cast it, otherwise look for a type converter and invoke it. If no type converter is found, throw an InvalidCastException.

TryCast/"as" keyword: If it is the correct type, cast it, otherwise return null.

DirectCast: If it is the correct type, cast it, otherwise throw an InvalidCastException.

After I have spelled out the above, some people have still responded that () is equivalent, so I will expand further upon why this is not true.

DirectCast only allows for either narrowing or widening conversions on the inheritance tree. It does not support conversions across different branches like () does, i.e.:

C# - this compiles and runs:

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

VB.NET - this does NOT COMPILE

'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)

The equivalent in VB.NET to my C# code is CType:

'This compiles and runs
Dim d As Double = 10
Dim i As Integer = CType(d, Integer)
Concretize answered 21/4, 2010 at 14:32 Comment(10)
+1 for defining the keywords and not making people look it up.Letterhead
string s = "10"; int i = (int)s; does not compile in C#.Draftsman
Have you tried using reflector to compare the MSIL?Brucite
@Robert: Oops! I rewrote with double... and actually ran my code through compilers this time :)Concretize
@Robert Davis: I think Collin Suave is not trying to run in Visual Studio his assumptionsCoping
@csauve: You're mistaken about what the keywords do. While int i = (int)d; may perform a known conversion for a known type as in your example code, in any scenario where you'd realistically use DirectCast in VB.NET (i.e., when an object's type is not known at compile time), casting with () in C# does not perform a conversion -- it just casts. So a boxed double cannot be unboxed as an integer. For this reason the two are effectively equivalent.Jameljamerson
@csauve: On the example you give, DirectCast is a false security blanket that is not worn all the time. First of all, if you really need to cast your variable to another compatible type, DirectCast is of no use. See my post here: #2684347Coping
I believe you might be interested in the System.Type method IsAssignableFrom.Mortmain
Out of curiousity, why do you need this?Pindaric
Your statements about how () is different than DirectCast are correct for value types, but incorrect for reference types. [The two are equivalent for reference types; no conversion is performed.] Which makes it a pointless comparison, because, IMHO, there is no reason to use DirectCast with a value type. The way I see it: () is equivalent to CType for value types, and to DirectCast for reference types.Bondholder
S
14

It seems clear that the functionality you want is not in C#. Try this though...

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;
}

Or, even though it is different from the VB, call it like:

static T DirectCast<T>(object o) where T : class
{
    T value = o as T;
    if (value == null && o != null)
    {
        throw new InvalidCastException();
    }
    return value;
}
Sphery answered 21/4, 2010 at 15:31 Comment(13)
agrees with what I originally thought. Thanks.Concretize
You can remove the T : class requirement if you use is instead of as. static T DirectCast<T>(object o) { if(o != null && o is T) return (T)o; else throw new InvalidCastException(); }Draftsman
In your code you don't use the "type" parameter, so you can leave it out. Or use it in an extra check: if (typeof(T) != type) throw new InvalidArgumentException(...);.Oberon
I was just noticing that same fact.Sphery
@Robert Davis: The where T : class is in there so I can use the 'as'. Using return(T)o seems to go against what csuave wants to accomplish.Sphery
@gbogumil, I would agree with Robert on this, you can use return (T)o at that point, because you can be absolutely sure that o is T -- it is impossible for the clr/compiler to funge things up and do type conversions.Concretize
I believe the equivalent of DirectCast actually is available in C#--just split into two different usages depending on the scenario. See my answer.Jameljamerson
@gbogumil: I don't mean to be antagonistic, but I think this answer is pretty misleading and misses the true relationship between DirectCast in VB.NET and casting in C#. Take a look at my answer and let me know what you think.Jameljamerson
@Robert Davis: Actually, the type checks on o are not necessary at all since (T)o will already throw an InvalidCastException if the types don't match up. With that in mind, the real equivalent code would just be return (T)o;, which makes it clear that the method itself could really just be the equivalent of (T)(object)value. Have a look at my answer.Jameljamerson
@Dan Tao: You're right. Since it's impossible for a type to create an implicit/explicit operator from object, (T)(object)value will never call any conversion functions.Draftsman
I don't think that type is T check does what you think it does. It's quite wrong, in fact. You're probably looking for type.IsInstanceOfType(o). Combining is with as is also redundant.Foilsman
This answer is very misleading indeed. When does !(type is T) ever return true?? Answer: probably not when you think. This statement is actually testing for when T is not a base class or interface of System.Type (not the corresponding type specified by the instance type, which I think was the intent).Mortmain
When working with reference types, C#'s (SomeReferenceType) is DirectCast. See "A cast operation between reference types does not change the run-time type of the underlying object; ..." in learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/…. That is to say, for reference types, that syntax never does any conversion. Therefore, given that you have the constraint where T : class, this code is unnecessary; simply use (SomeReferenceType)Bondholder
J
8

SECOND UPDATE:

OK, here's a C# method that's been proposed to allegedly do basically what DirectCast does in 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;
}

Here are the problems with the above method:

  1. It has a where T : class constraint, which DirectCast does not.
  2. It boxes its argument as a System.Object -- again, not true of DirectCast (at least not that I'm aware of).
  3. It uses as unnecessarily (which is why it has the class constraint in the first place); calling (T)o will throw an InvalidCastException if it doesn't work; why check if the value matches up using as, only to throw the same exception that would've been thrown if you'd gone the (T)o route to begin with?

The method could really be rewritten to provide the same results as DirectCast as follows:

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

Funny observation: really all this method is doing is boxing a value and then attempting to unbox it. In other words, DirectCast<int>(12.0) would really be the same as (int)(object)12.0 (and either would throw an exception). Realizing this makes the proposed DirectCast<T> method pretty unnecessary altogether.

Now, here's an example of how DirectCast and casting with () are "different" between VB.NET and 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, so one compiles, the other doesn't. But look at that code. What's the point of DirectCast when you already know an object's type? This is not a realistic comparison, because in VB.NET there'd never be any reason to call DirectCast like the code above does. (If you wanted to convert a value known to be of type System.Int32 to a value of type System.Int64 in VB.NET, you'd use CLng, not DirectCast.) If there were a variable typed as System.Object in there, then it would make sense to use DirectCast, and the below code would indeed be equivalent:

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

So I maintain that DirectCast in VB.NET, in any scenario in which it actually makes sense to use it (i.e., when the type of an object is not known at compile time), is the same as a straight ()-style cast in C#.


EDIT: Well, shame on me for posting some VB code that didn't compile. After reconsidering what I was saying, I withdraw my second answer but maintain the first.

If you're referring to the usage of DirectCast where you take an object of unknown type and try to cast it to the desired type, then it is the same as C#'s () cast:

VB:

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

C#:

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

This is because, if o is typed as a System.Object, then the () operation in C# will attempt to unbox it. This will fail if the types don't match exactly; for instance, if o is a boxed System.Double, then (int)o will throw an exception because o must be unboxed as a System.Double before it can be converted to a System.Int32 (if you don't believe me, try it out for yourself!).


Note: the below is inaccurate because DirectCast does not perform widening conversions; in any case, I'm leaving it for posterity.

On the other hand, when dealing with widening vs. narrowing conversions, using the () operation in C# does more work than simply casting, as you've pointed out (i.e., you can do (int)someDouble). In this scenario, DirectCast is equivalent to plain old assignment in 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;
Jameljamerson answered 21/4, 2010 at 16:6 Comment(3)
You're vb code doesn't compile. I'm assuming that instead of 12 as the type param to directcast you meant long, but that doesn't compile either. Also make sure that option strict is on, or else youre going to get a crap-ton of implicit conversions (the vb compiler likes to read minds)Concretize
@csauve: Good call. That was stupid of me! In any case, take a look at my first point. I do believe that DirectCast is really the same as C#'s () operation when used on objects typed as System.Object.Jameljamerson
@csauve: By the way, regarding Option Strict... in the code I originally posted, Option Infer works just as well. (I've added the types anyway, for completeness.)Jameljamerson
C
1

Actually the compiler just catches the DirectCast violation if it infers that the typed variable cannot be converted to the other type.

These are the actual equivalents:

double d = 10;
int i = (int)d;
Dim d As Double = 10
Dim i As Integer = d

Note the dangerousness of this construct. When you just merely assign double to integer in VB.NET, the double will be accidentally downsized to integer.

Whereas C# programmers get the compile-time safety of not accidentally downsizing a variable in .NET. VB.NET programmers have to contend with always using DirectCast as a safe programming habit.

These are the actual equivalents:

// 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)

Regarding Dan Tao's comment:

There's no need to use DirectCast in C#. The runtime also prevents loading of long to integer value. This is what the OP is contending, that C# doesn't have DirectCast, that DirectCast can prevent assigning of different types of variable, whereas "because" C# doesn't have this DirectCast, it will silently error on assigning different types. But as you can see, that's not the case. C#'s casting is exactly the same as DirectCast. This will cause a InvalidCastException runtime error:

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

This will also cause the same runtime error as above:

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

Now, this is where the "fun" part comes in. With VB.NET you have to remember many keywords in order to accomplish something. In C#, if a given keyword could be used in another scenario (like in this one downcasting of variable), they will not invent another keyword just to make it happen.

In C# you just have to do this:

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

In VB.NET if you really want to downcast the variable, and want the orthogonal way to do it, i.e. just remembering one keyword, you must do this:

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

But that will not compile, so how can we achieve downcasting long to integer? You must remember VB.NET's other keywords. Whereas in C#, it's orthogonal, you unbox variable using this construct (typehere), you also downcast/upcast using same construct (typehere). In VB.NET there's a fundamental disconnect between loading a value from object and downcasting it. So in VB.NET, you have to do this:

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

Hmm.. I think the OP's confusion stems from C# multiple use of (typehere). First, it is used for downcasting; second, the same construct (check the first part of this post, object o = l) is also used for unboxing of value from object, which, rest assured, it has DirectCast's safe type conversion behavior. They are the same!

This downcasting...

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

...is not equivalent to:

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

If you want to perform downcasting, you have to do this:

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

Now, if a VB.NET programmer is programming by intent, and not sleepy while coding, why will he/she use DirectCast when he/she is fully aware that it cannot assign different types? If what the VB.NET programmer really wanted is to downcast, he/she should not attempt DirectCast in the first place. Now the VB.NET programmer, upon discovering that DirectCast cannot be used for downcasting, must backspace what he/she has written and replace it with CInt (or CType).

Coping answered 21/4, 2010 at 15:59 Comment(5)
Try turning Option Strict On -- I would hope that most shops force any vb devs to write with it on.Concretize
@Michael: This is what I thought, too; but as csauve pointed out in a comment to my answer, the code examples at the end of your answer are actually not equivalents. A long can be assigned to an int in C#, but VB.NET will not allow a Long to be assigned to an Integer using DirectCast.Jameljamerson
@Dan Tao - See my comment aboveCoping
Our answers have somewhat converged ;) But my point was specifically in response to your code double d = 10; int i = d; -- you rightly commented that it would not compile, just as DirectCast(d, Integer) would not compile; on the other hand, in C#, int i = 10; long l = i; will compile in C#, but DirectCast(i, Long) will not. Otherwise, I think we're completely on the same page.Jameljamerson
@Dan Tao: regarding DirectCast(i, Long). I think VB.NET language designers doesn't want their programmers to suffer. Why they will not allow VB.NET programmers to use this (they allow)? --> Dim l As Long = i. I think all in all, DirectCast are just meant to be backspaced when they are accidentally used(because of wrong usage expectation) by programmers, when what they really meant after all is CInt/CLng, CType, etcCoping
C
1

VB.NET:

Dim xxx as label = Directcast(sender, label)

C#:

label xxx = (label)sender;
Cauca answered 28/8, 2019 at 18:14 Comment(2)
Welcome to StackOverflow. please explain your code and how this code solves the problem.Parasiticide
You would do well to read the question in full as well as the existing answers when you are necroing an 8-year old question.Concretize
C
0

Did you really try to run your sample code?

Regarding...

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

...You posited that it will run. It also does not run.

Coping answered 21/4, 2010 at 14:33 Comment(1)
I've editted it to use doubles. I had the illconceived notion that string->int was legal in c#. double->int shoudl workConcretize
D
0

You have two types of cast in C#. Without additional code there is no equivalent to the DirectCast keyword in C#. The closest you have without creating it yourself is to use ().

You have:

My_Object c = (My_Object)object

and

My_Object c = object as My_Object

In the first one, if the cast fails, it throws an error. You are saying, "I know what this object is, and if it is not, there is something wrong."

In the second, c is assigned the value null where possible (null can't be assigned to value types). In this one you are saying "I think I know what this one is, but if not don't throw an error, because nothing might be wrong."

Other post explaining casting:

What is the difference between explicit and implicit type casts?

Deuterium answered 21/4, 2010 at 14:38 Comment(2)
oops I got my terminology mixed up. ThanksDeuterium
As stated above, () casts allow for type converters, DirectCast does not.Concretize
M
0

Let me try to give a shot at this.

First, let me be clear on this. This WILL NOT compile:

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

VB's CType

In VB, you would use:

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

In C#, I would use:

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

VB's DirectCast

If it is the correct type, cast it, otherwise throw an InvalidCastException.

Direct cast can only go up or down a branch, never across to a different one.

From that explanation, it would be a direct equivalent of the C# cast. However in C# you would only need to specify the cast operator only for casting down. Casting up is totally optional. Example:

// 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 does not look for any Type Converter. It will only look for any defined explicit / implicit operator overload for the type you are trying to cast to.


VB's TryCast

You already understood correctly that this is equivalent to C# as keyword.

Mose answered 21/4, 2010 at 15:42 Comment(3)
C# () cast allows type converters. DirectCast does not. Replace string with double and you will see. C# allows double->int using () cast, vb.net does not allow it with directcast, only with ctype.Concretize
() cast does not use type converters. It acts on the IConvertible interface. See for yourself on MSDN's TypeConverter page: msdn.microsoft.com/en-us/library/…. There's no single example showing that you are able to use C# cast to trigger the usage of TypeConverters.Mose
You are correct, and I was wrong, that "C# cast does not look for any Type Converter. It will only look for any defined explicit / implicit operator overload for the type you are trying to cast to." Which makes it a thing all to its own - its not quite a ctype, and not quite a directcast.Concretize
C
0

You can implement it yourself:

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

Use it as follows:

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

This works and doesn't involve user-defined converters because of the fact that generics are "resolved" at runtime, but type conversions are resolved at compile-time - the framework doesn't actually generate distinct implementations for each T, but rather shares the implementation for similar T, and hence the runtime doesn't have the information to resolve the custom conversions.

Cenacle answered 21/4, 2010 at 16:1 Comment(0)
C
0

DirectCast and () do not always generate the same CIL, so I think this is a difference between the VB and C# compilers.

As far as I can tell, reference types get cast using the castclass CIL instruction, while for value types the compiler generates the relevant CIL depending on the input types.

In C#, casting from double to integer emits the conv.i4 CIL instruction, which will cheerfully overwrite the sign bit or whatever in the output, if the value is too large. In Visual Basic, it's a compilation error.

Interestingly, if you use an intermediate object variable to hold the double, then the cast will fail for both C# and Visual Basic... but at runtime. Both compilers emit an unbox instruction instead of trying to do a conversion.

Celka answered 19/9, 2018 at 8:16 Comment(0)
L
-1

The () casting should be the same; it throws an InvalidCastException. Just try this in C#:

 string t = "hello";
 object x = t;
 int j = (int) x;
Lumberman answered 21/4, 2010 at 14:38 Comment(4)
Yes, but the "(int) x" is actually attempting to convert a string to int, so if t = "10" it would succeed. DirectCast does not allow for type converters, so even if t = "10" it would still fail.Concretize
@Collin Sauve: I'm not sure where you got that idea from, but it's wrong. It simply won't compile if you try to do int j = (int) t where t = "10".Mose
sorry. strings are just a bad example. double->int is a better one. double d = 10; int i = (int)d; this is all well and good in c#, but if i replace the (int)d with directcast(d,integer) it doesn't work in vb.netConcretize
I think Collin Sauve is not trying to run the assumed non-counterpart of C# from VB.NET. If t = "10" (string), the above code will also result to InvalidCastException. @CollinSauve: please do run the code in Visual Studio before making assumptionsCoping
C
-1

I think this scenario sums it up best why DirectCast has a false sense of compile-time type checking security for non-object (object keyword) type, and is just meant to be backspaced.

float f = 10;
long l = f;
Option Strict On
Dim f As Single = 10
Dim l As Long = f

A C# coder, upon discovering that float is not directly assignable to long and won't compile, will do this:

long l = (long)f;

Which is correct.

Now, let's turn to our VB.NET coder, upon discovering that float is not assignable to long and won't compile, will attempt this:

Dim l As Long = DirectCast(f, Long)

A few seconds later...

VB.NET programmer: "Please let me do my bidding, please compile, please...!!!"

After some Googling-fu and MSDN-browsing moments later:

VB.NET programmer: "Ah.. so I have to use this CLng or CType construct for casting variables"

Dim l As Long = CLng(f)

That is what I meant by DirectCast having a false sense of compile-time type checking security. DirectCast are just meant to be backspaced if a programmer doesn't know when and where they should be used. DirectCast is a security blanket that is not worn all the time.

How useful is DirectCast in this scenario if it will not be used after all?


[EDIT]

@Jules

I'm not purporting that all VB.NET programmers don't know what's the real use of DirectCast. Some of them really do know that DirectCast are just meant to be used for object types (and primitive types that are boxed in object) only.

One scenario where a VB.NET coder recoding existing C# code to VB.NET one will arrive at wrong conclusion, is with expected (be it rightly or not) languages symmetry to each other.

When he/she sees in the code this construct...

TextBox txt = (TextBox)sender;

...He/she will translate that to this:

Dim txt As TextBox = DirectCast(sender, TextBox)

Which is correct.

Now, because we programmers love symmetry, some of us (I might be too if I don't know CLng) will tend to convert this 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

...to this:

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

If a C# person is the one converting C# code to VB.NET, he will be frustrated for apparent lack of symmetry here.

But for a VB.NET person tasked to convert C# code to VB.NET, he will get the impression that C# compiler doesn't catches incompatible type assignments, while VB.NET catches it. Now, for that apparent discovery, will brag that VB.NET feature to his colleagues and some forums.

But lest be the VB.NET programmer makes the mistake of wrongly inferring the intent of first code. The C#'s code fragment above started its life like this was initially written like this:

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

And that will not compile, C# compiler catches incompatible type assignments, in the same vein that the equivalent VB.NET with Option Strict On will also not compile that (albeit will only not compile when Option Strict is set to On, too lenient). So we need to typecast float to long using (long). Becomes this: long l = (long)f;

Now for casting one variable type to another compatible type, in the same vein that we convert this code...

TextBox txt = (TextBox)sender;

...to this code:

Dim txt As TextBox = DirectCast(sender, Textbox)

We must convert this code...

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

...to this code:

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

But alas, that will not compile. On casting between compatible primitive types, this is where DirectCast fells short in. It doesn't offer any symmetry to the C# code above, and it cannot be used on casting compatible primitive types, despite its name DirectCast.

The way I see it, DirectCast should be named CastObject, since it can only cast between object types (and also primitive types that are boxed in object) anyhow. DirectCast really has no business with assigning compatible primitive types (integer, double, and their lower and higher counterpart). When assigning between compatible primitive types, DirectCast ceases to be useful, especially you will backspace it anyhow, and replace it with proper one.

Or the other way I see it, DirectCast construct should be amended, so it can cast compatible types like the way old and newer languages do ever since, e.g., C, C++, C#, Java, Delphi, D, etc. Doing this, it will offer VB.NET significant symmetry to other languages when it comes to type casting. Doing this, we can also throw away (hypothetically only, we cannot make other programs fail that rely on old functions) all plethora of functions which names doesn't directly maps to its types (e.g., CInt, CDbl, CSng, etc.). We will just use DirectCast in lieu of them.

Coping answered 21/4, 2010 at 19:1 Comment(1)
What are you talking about?? I know exactly when and when not to use DirectCast. You may as well say a VB.Net coder types: Dim l as long = Gosub(f, Long)!Peden

© 2022 - 2024 — McMap. All rights reserved.