Direct Casting VS 'As' Operator

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.

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.

Difference between using Cast and AS operator in following code?

as operator would not raise an error but cast will raise an error of InvalidCastException

From MSDN

The as operator is like a cast except that it yields null on conversion failure instead of raising an exception.

expression as type

is equivalent to:

expression is type ? (type)expression : (type)null

except that expression is evaluated only once.

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 using cast expressions.

Which is the best practice in C# for type casting?

At least there are two possibilities for casting, one for type checking and a combination of both called pattern matching. Each has its own purpose and it depends on the situation:

Hard cast

var myObject = (MyType)source;

You normally do that if you are absolutely sure if the given object is of that type. A situation where you use it, if you subscribed to an event handler and you cast the sender object to the correct type to work on that.

private void OnButtonClick(object sender, EventArgs e)
{
var button = (Button)sender;

button.Text = "Disabled";
button.Enabled = false;
}

Soft cast

var myObject = source as MyType;

if (myObject != null)
// Do Something

This will normally be used if you can't know if you really got this kind of type. So simply try to cast it and if it is not possible, simply give a null back. A common example would be if you have to do something only if some interface is fullfilled:

var disposable = source as IDisposable;

if(disposable != null)
disposable.Dispose();

Also the as operator can't be used on a struct. This is simply because the operator wants to return a null in case the cast fails and a struct can never be null.

Type check

var isMyType = source is MyType;

This is rarely correctly used. This type check is only useful if you only need to know if something is of a specific type, but you don't have to use that object.

if(source is MyType)
DoSomething();
else
DoSomethingElse();

Pattern matching

if (source is MyType myType)
DoSomething(myType);

Pattern matching is the latest feature within the dotnet framework that is relevant to casts. But you can also handle more complicated cases by using the switch statement and the when clause:

switch (source)
{
case SpecialType s when s.SpecialValue > 5
DoSomething(s);
case AnotherType a when a.Foo == "Hello"
SomethingElse(a);
}

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.

Why does a direct cast fail but the as operator succeed when testing a constrained generic type?

I used this question as the basis for a blog article in October 2015. Thanks for the great question!

Could someone please explain why this is the case?

"Why" questions are hard to answer; the answer is "because that's what the spec says" and then the natural question is "why does the spec say that?"

So let me make the question more crisp:

What language design factors influenced the decision to make the given cast operator illegal on constrained type parameters?

Consider the following scenario. You have a base type Fruit, derived types Apple and Banana, and, now comes the important part, a user-defined conversion from Apple to Banana.

What do you think this should do when called as M<Apple>?

void M<T>(T t) where T : Fruit
{
Banana b = (Banana)t;
}

Most people reading the code would say that this should call the user-defined conversion from Apple to Banana. But C# generics are not C++ templates; the method is not recompiled from scratch for every generic construction. Rather, the method is compiled once, and during that compilation the meaning of every operator, including casts, is determined for every possible generic instantiation.

The body of M<Apple> would have to have a user-defined conversion. The body of M<Banana> would have an identity conversion. M<Cherry> would be an error. We cannot have three different meanings of an operator in a generic method, so the operator is rejected.

Instead what you have to do is:

void M<T>(T t) where T : Fruit
{
Banana b = (Banana)(object)t;
}

Now both conversions are clear. The conversion to object is an implicit reference conversion; the conversion to Banana is an explicit reference conversion. The user-defined conversion is never called, and if this is constructed with Cherry then the error is at runtime, not compile time, as it always is when casting from object.

The as operator is not like the cast operator; it always means the same thing no matter what types it is given because the as operator does not ever invoke a user-defined conversion. Therefore it can be used in a context where a cast would be illegal.

When to use either type casting or the as operator in AS3?

You cast only when you are certain the cast will succeed. If casting fails a runtime error is thrown.
You use 'as' to produce a soft cast that will never throw an error. In that case either the cast succeed or the default value of the datatype is returned (for most object that is null).
Both casts are meant to be used in very different situations but since they are misunderstood often you will see 'as' being used when the coder really meant a direct cast.
If following a cast the coder will not check or need to check if the cast has succeeded then it should have used a direct cast. If following a cast the coder needs to check if the cast has succeeded, he should use 'as'.

It should be explained why hitman answer is not correct. The provided code assume success:

(getChildAt(i) as TextField).text=i.toString();

Meaning coder knows the display list only contains TextField object (or else an error will occur). In that case a direct cast is recommended:

TextField(getChildAt(i)).text = i.toString();

If the display list contains other object types then 'as' can be used:

var field:TextField = getChildAt(i) as TextField;
if(field)//if null then getChildAt(i) is not a TextField
{
//field exist so cast succeeded
}

c++: cast operator vs. assign operator vs. conversion constructor priority

The statement t1 = t2; is equivalent to:

t1.operator=(t2);

Now the usual rules of overload resolution apply. If there's a direct match, that's the chosen one. If not, then implicit conversions are considered for use with the (automatically generated, "implicitly defined") copy-assignment operator.

There are two possible implicit, user-defined conversions. All user-defined conversions count equal, and if both are defined, the overload is ambiguous:

  • Convert t2 to a Test1 via the Test1::Test1(Test2 const &) conversion constructor.

  • Convert t2 to a Test1 via the Test2::operator Test1() const cast operator.

What is difference between normal typecasting and using “AS” keyword

Using as will fail gracefully if the object is the wrong type, and the resulting value will be null, where a normal cast would throw an InvalidCastException:

object x = new object();
string y = x as string; // y == null
string z = (string)x; // InvalidCastException

What is a difference between casts: (A) x and x as A?

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

You can use the as operator to perform certain types of conversions between compatible reference types or nullable types.

Consider the following example:

expression as type

The code is equivalent to the following expression except that the expression variable is evaluated only one time.

expression is type ? (type)expression : (type)null

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

Refer:
as (C# Reference)



Related Topics



Leave a reply



Submit