C#'s equivalent to VB.NET's DirectCast?

2020-02-08 05:02发布

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)

11条回答
孤傲高冷的网名
2楼-- · 2020-02-08 05:21

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

查看更多
霸刀☆藐视天下
3楼-- · 2020-02-08 05:23

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.

查看更多
在下西门庆
4楼-- · 2020-02-08 05:28

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;
}
查看更多
做个烂人
5楼-- · 2020-02-08 05:29

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 variable .NET. VB.NET programmers have to contend with always putting 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) 

[EDIT]

@Dan Tao:

There's no need to use DirectCast in C#, the runtime also prevents loading of long to integer value. This is what csauve is contending, that C# don'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 InvalidCastException runtime error:

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

This will also cause 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 to 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 csauve 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 he will use DirectCast when he is fully aware that it cannot assign different types? If what the VB.NET programmer really wanted is to downcast, he should not attempted DirectCast in the first place. Now the VB.NET programmer, upon discovering that DirectCast cannot be used for downcasting, must backspace what he written and replace it with CInt (or CType)

查看更多
手持菜刀,她持情操
6楼-- · 2020-02-08 05:31

You can implement it yourself:

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

Usable 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.

查看更多
beautiful°
7楼-- · 2020-02-08 05:34

I think this scenario sums it best why DirectCast has 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 has 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 DirectCast is 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 sees in the code this construct...

TextBox txt = (TextBox)sender;

...He 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, 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.

查看更多
登录 后发表回答