C# "As" Cast VS Classic Cast

C# as cast vs classic cast

With the "classic" method, if the cast fails, an InvalidCastException is thrown. With the as method, it results in null, which can be checked for, and avoid an exception being thrown.

Also, you can only use as with reference types, so if you are typecasting to a value type, you must still use the "classic" method.

Note:

The as method can only be used for types that can be assigned a null value. That use to only mean reference types, but when .NET 2.0 came out, it introduced the concept of a nullable value type. Since these types can be assigned a null value, they are valid to use with the as operator.

Casting vs using the 'as' keyword in the CLR

The answer below the line was written in 2008.

C# 7 introduced pattern matching, which has largely replaced the as operator, as you can now write:

if (randomObject is TargetType tt)
{
// Use tt here
}

Note that tt is still in scope after this, but not definitely assigned. (It is definitely assigned within the if body.) That's slightly annoying in some cases, so if you really care about introducing the smallest number of variables possible in every scope, you might still want to use is followed by a cast.


I don't think any of the answers so far (at the time of starting this answer!) have really explained where it's worth using which.

  • Don't do this:

    // Bad code - checks type twice for no reason
    if (randomObject is TargetType)
    {
    TargetType foo = (TargetType) randomObject;
    // Do something with foo
    }

    Not only is this checking twice, but it may be checking different things, if randomObject is a field rather than a local variable. It's possible for the "if" to pass but then the cast to fail, if another thread changes the value of randomObject between the two.

  • If randomObject really should be an instance of TargetType, i.e. if it's not, that means there's a bug, then casting is the right solution. That throws an exception immediately, which means that no more work is done under incorrect assumptions, and the exception correctly shows the type of bug.

    // This will throw an exception if randomObject is non-null and
    // refers to an object of an incompatible type. The cast is
    // the best code if that's the behaviour you want.
    TargetType convertedRandomObject = (TargetType) randomObject;
  • If randomObject might be an instance of TargetType and TargetType is a reference type, then use code like this:

    TargetType convertedRandomObject = randomObject as TargetType;
    if (convertedRandomObject != null)
    {
    // Do stuff with convertedRandomObject
    }
  • If randomObject might be an instance of TargetType and TargetType is a value type, then we can't use as with TargetType itself, but we can use a nullable type:

    TargetType? convertedRandomObject = randomObject as TargetType?;
    if (convertedRandomObject != null)
    {
    // Do stuff with convertedRandomObject.Value
    }

    (Note: currently this is actually slower than is + cast. I think it's more elegant and consistent, but there we go.)

  • If you really don't need the converted value, but you just need to know whether it is an instance of TargetType, then the is operator is your friend. In this case it doesn't matter whether TargetType is a reference type or a value type.

  • There may be other cases involving generics where is is useful (because you may not know whether T is a reference type or not, so you can't use as) but they're relatively obscure.

  • I've almost certainly used is for the value type case before now, not having thought of using a nullable type and as together :)


EDIT: Note that none of the above talks about performance, other than the value type case, where I've noted that unboxing to a nullable value type is actually slower - but consistent.

As per naasking's answer, is-and-cast or is-and-as are both as fast as as-and-null-check with modern JITs, as shown by the code below:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
const int Size = 30000000;

static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i + 1] = "x";
values[i + 2] = new object();
}
FindLengthWithIsAndCast(values);
FindLengthWithIsAndAs(values);
FindLengthWithAsAndNullCheck(values);
}

static void FindLengthWithIsAndCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = (string) o;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and Cast: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}

static void FindLengthWithIsAndAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = o as string;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and As: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}

static void FindLengthWithAsAndNullCheck(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
string a = o as string;
if (a != null)
{
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("As and null check: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
}

On my laptop, these all execute in about 60ms. Two things to note:

  • There's no significant difference between them. (In fact, there are situations in which the as-plus-null-check definitely is slower. The above code actually makes the type check easy because it's for a sealed class; if you're checking for an interface, the balance tips slightly in favour of as-plus-null-check.)
  • They're all insanely fast. This simply will not be the bottleneck in your code unless you really aren't going to do anything with the values afterwards.

So let's not worry about the performance. Let's worry about correctness and consistency.

I maintain that is-and-cast (or is-and-as) are both unsafe when dealing with variables, as the type of the value it refers to may change due to another thread between the test and the cast. That would be a pretty rare situation - but I'd rather have a convention which I can use consistently.

I also maintain that the as-then-null-check gives a better separation of concerns. We have one statement which attempts a conversion, and then one statement which uses the result. The is-and-cast or is-and-as performs a test and then another attempt to convert the value.

To put it another way, would anyone ever write:

int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}

That's sort of what is-and-cast is doing - although obviously in a rather cheaper way.

Direct casting vs 'as' operator?


string s = (string)o; // 1

Throws InvalidCastException if o is not a string. Otherwise, assigns o to s, even if o is null.

string s = o as string; // 2

Assigns null to s if o is not a string or if o is null. For this reason, you cannot use it with value types (the operator could never return null in that case). Otherwise, assigns o to s.

string s = o.ToString(); // 3

Causes a NullReferenceException if o is null. Assigns whatever o.ToString() returns to s, no matter what type o is.


Use 1 for most conversions - it's simple and straightforward. I tend to almost never use 2 since if something is not the right type, I usually expect an exception to occur. I have only seen a need for this return-null type of functionality with badly designed libraries which use error codes (e.g. return null = error, instead of using exceptions).

3 is not a cast and is just a method invocation. Use it for when you need the string representation of a non-string object.

What is a cast under the hood

You can use IL Dissasembler to see what is being produced by your code on a lower level. If you have Visual Studio installed on your machine, you should be able to find it just by typing "ildasm" in the windows search box.

Here's what the IL of the following code looks like:

object myObj = "abc";
string myStr = (string)myObj;
string tostring = myObj.ToString();

Sample Image

What is the difference between these two C# statements?

"The as operator is like a cast operation. However, if the conversion is not possible, as returns null instead of raising an exception"

"Note that the as operator only performs reference conversions and boxing conversions. The as operator cannot perform other conversions, such as user-defined conversions, which should instead be performed by using cast expressions."

Read More

Cast vs Serialize/Deserialize C#

First of all, boxing is not what's happening here. Boxing refers to the operation of preparing value types to be put on the heap as reference types. If your dictionary was a value type, it would indeed need to be boxed and unboxed.

Alas, a dictionary is a reference type. And that means there will be no boxing. The reference will be passed as is to your method. What your method will have to do is a downcast to a dictionary. While this requires the RTTI (run time type information) of your object to be evaluated, the performance impact will usually not be noticed (except in extreme cases).

Your serialization/deserialization solution however is much more expensive. You will have to allocate space for a string on the heap, you have to serialize, deserialize and the string will eventually need to be garbage collected. You can expect that to be far more expensive than a simple downcast.

UPDATE:

Just to make that clear, you can still expect boxing to be cheaper than serialization. So even for value types there is no reason choose serialization. Especially since there is a pretty high chance you will have at least one boxing operation in the whole serialization round trip.

Is it better to cast double as decimal or construct new decimal from double?

There's really no difference. Using ILSpy:

// decimal
public static explicit operator decimal(double value)
{
return new decimal(value);
}

Based on examining the IL with LINQPad, the operator overload is called the same way in checked and unchecked contexts in C#.

In Visual Basic, CDec([double]) and CType([double],Decimal) calls the constructor directly, so there's no difference in that regard, but the cast is more able to participate in compile-time constant-folding than the constructor.



Related Topics



Leave a reply



Submit