Does It Make Sense to Use "As" Instead of a Cast Even If There Is No Null Check

Does it make sense to use as instead of a cast even if there is no null check?

Your understanding is true. That sounds like trying to micro-optimize to me. You should use a normal cast when you are sure of the type. Besides generating a more sensible exception, it also fails fast. If you're wrong about your assumption about the type, your program will fail immediately and you'll be able to see the cause of failure immediately rather than waiting for a NullReferenceException or ArgumentNullException or even a logical error sometime in the future. In general, an as expression that's not followed by a null check somewhere is a code smell.

On the other hand, if you are not sure about the cast and expect it to fail, you should use as instead of a normal cast wrapped with a try-catch block. Moreover, use of as is recommended over a type check followed by a cast. Instead of:

if (x is SomeType)
((SomeType)x).SomeMethod();

which generates an isinst instruction for the is keyword, and a castclass instruction for the cast (effectively performing the cast twice), you should use:

var v = x as SomeType;
if (v != null)
v.SomeMethod();

This only generates an isinst instruction. The former method has a potential flaw in multithreaded applications as a race condition might cause the variable to change its type after the is check succeeded and fail at the cast line. The latter method is not prone to this error.


The following solution is not recommended for use in production code. If you really hate such a fundamental construct in C#, you might consider switching to VB or some other language.

In case one desperately hates the cast syntax, he/she can write an extension method to mimic the cast:

public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
return (T)o;
}

and use a neat[?] syntax:

obj.To<SomeType>().SomeMethod()

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.

What's the point in using is followed by as instead of as followed by a null check in C#?

To answer your actual question: Experience and habit.

Before the inclusion of the as keyword in .Net 2.0, the only way to safely determine if an object could be cast to a specific type/interface was with the is keyword.

So, people got in the habit of using is before attempting an explicit cast in order to avoid unnecessary exceptions. This led to the pattern you have second in your list of samples:

if(whatever is IDisposable)  //check
{
((IDisposable)whatever).Dispose(); //cast - won't fail
}

Then, we got the safe-cast as keyword. I would guess that most people, when they first started using as, continued to use the familiar pattern, but replaced the direct cast with a safe-cast, and their pattern of choice morphed into your example 1. (I know I did this for awhile.)

if(whatever is IDisposable) 
{
(whatever as IDisposable).Dispose();
}

Eventually, many or most either realized on their own, or were instructed by fxCop or CodeAnalysis that the 'proper' pattern is your example 3:

IDisposable whateverDisposable = whatever as IDisposable;
if(whateverDisposable != null )
{
whateverDisposable.Dispose();
}

Certainly there are some floating around who are at the example 1 stage still and haven't yet 'evolved' their pattern to your example 3 for some reason, or others who are still just using the good-old time-proven pattern of using is followed by a direct cast.

is' versus try cast with null check

Because there's only one cast. Compare this:

if (myObj.myProp is MyType) // cast #1
{
var myObjRef = (MyType)myObj.myProp; // needs to be cast a second time
// before using it as a MyType
...
}

to this:

var myObjRef = myObj.myProp as MyType; // only one cast
if (myObjRef != null)
{
// myObjRef is already MyType and doesn't need to be cast again
...
}

C# 7.0 supports a more compact syntax using pattern matching:

if (myObj.myProp is MyType myObjRef)
{
...
}

No Exception while type casting with a null in java

You can cast null to any reference type without getting any exception.

The println method does not throw null pointer because it first checks whether the object is null or not. If null then it simply prints the string "null". Otherwise it will call the toString method of that object.

Adding more details: Internally print methods call String.valueOf(object) method on the input object. And in valueOf method, this check helps to avoid null pointer exception:

return (obj == null) ? "null" : obj.toString();

For rest of your confusion, calling any method on a null object should throw a null pointer exception, if not a special case.

Using an is check when with an explicit cast

FxCop issues Warning CA1800 in the first scenario (and not only when using as, but also when using an unchecked cast) as both is and the actual casts require certain type checking operations to determine whether the cast is successful or whether to throw an InvalidCastException.

You might save a few operations by just using as once and then checking the result for null if you are going to use the cast value anyway, rather than checking explicitly with is and then casting anew.

Why or why not use instanceof to check if not null

That depends on what you are actually checking

  • If you just want to know whether the reference is null, use x == null and nothing else

  • If you want to know whether a particular reference is not null and points to an instance of type Foo you may use x instanceof Foo as it implies being not null

    But if the compile-time type of the reference is already Foo, you know that the instance is of type Foo when it is non-null, therefore the first bullet applies in this case; you just want to test for null. This is the case in your updated question’s example, the reference already has the compile-time type View.

  • If you want to know whether a type cast (Foo)x will succeed, you may use x == null || x instanceof Foo as the type cast will also succeed when x is null (however, you should think twice whether you really want to accept nulls, even if the type cast will be successful)

What's the use of casting NULL to SomeType* in C++?

What's the use of casting NULL to SomeType* in C++?

SomeClass* someClassPtr = static_cast<SomeClass*>(NULL);

The cast has no effect in this context.

However more generally, it does make sense in some other context where overloading is involved:

void stupidly_overloaded_function(int);
void stupidly_overloaded_function(SomeClass*);
void stupidly_overloaded_function(SomeOtherClass*);

stupidly_overloaded_function(NULL);

which function gets called? It actually depends on the definition of NULL. Either it calls the int overload, or compilation fails due to ambiguity.

A cast can disambiguate the call:

stupidly_overloaded_function(static_cast<SomeClass*>(NULL));

I think this is a significant reason for introducing nullptr. Although even nullptr cannot deal with multiple different pointer overloads, it does disambiguate between integers:

void not_quite_as_silly_overload(int);
void not_quite_as_silly_overload(SomeClass*);

not_quite_as_silly_overload(NULL); // might call either overload
not_quite_as_silly_overload(nullptr); // unambiguously calls the pointer overload

Another case in C is macros involved with sizes of objects, such as the container_of macro from the Linux kernel:

define container_of(ptr, type, member) ({                      \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

Here, just 0 is used instead of NULL. I think that this could be implemented in C++ without the cast, but only using declval which was introduced in C++11.



Is this simply old-style (or even C-style) code from before there was nullptr?

No, there was no universal style for such redundant casts. Your example has no overloading, so the above case does not apply to it.

Is/was casting NULL necessary for other things than down-/upcasting when working with inheritance?

No, it was (and still is in the case of different pointer overloads) necessary for overload resolution.

What's the point of As keyword in C#

They aren't two system of casting. The two have similar actions but very different meanings. An "as" means "I think this object might actually be of this other type; give me null if it isn't." A cast means one of two things:

  • I know for sure that this object actually is of this other type. Make it so, and if I'm wrong, crash the program.

  • I know for sure that this object is not of this other type, but that there is a well-known way of converting the value of the current type to the desired type. (For example, casting int to short.) Make it so, and if the conversion doesn't actually work, crash the program.

See my article on the subject for more details.

https://ericlippert.com/2009/10/08/whats-the-difference-between-as-and-cast-operators/

Using as and expecting a null return

Yes, it's the right way to do it (considering "applying changes" will need to use the cast result).

However, if you are not going to use the cast return value, you can simply use is.

Using is and then casting again with the cast operator (parenthesis) is frowned upon.



Related Topics



Leave a reply



Submit