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

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

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;

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.


VB.NET:

Dim xxx as label = Directcast(sender, label)

C#:

label xxx = (label)sender;

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