about casting

T

Tony Johansson

Hello!

Here I have two different way to use casting.
Is any of these any better then the other?
Or is it just a question of taste.

newCards.Add((Card)sourceCard.Clone());
newCards.Add(sourceCard.Clone() as Card);

//Tony
 
J

Jon Skeet [C# MVP]

Tony Johansson said:
Here I have two different way to use casting.
Is any of these any better then the other?
Or is it just a question of taste.

newCards.Add((Card)sourceCard.Clone());
newCards.Add(sourceCard.Clone() as Card);

Using "as" doesn't throw an exception if the cast fails - it just
returns null instead.

Using a direct cast also allows for user-defined conversions etc.

I tend to use "as" for values I'm in doubt about - when I'm about to
test the result:

Object unknown = GetMeSomeData();
string text = unknown as string;
if (text != null)
{
...
}


I use direct casting when I really, really expect it to succeed - i.e.
when a failure indicates a problem which should be reported as an
exception:

Object shouldBeString = GetMeSomeText();
string text = (string) shouldBeString;
 
M

Marc Gravell

One other note; "as" can only be used with reference types and
Nullable<T>, as it can't return null for a regular struct. You'd need
to use "is" and cast to achieve the same.

Marc
 
H

Hilton

Tony,

I use "as" because it is much faster than the "(Card)" variety of casting.
Also, I think "(vehicle as Car).Color" looks a lot cleaner than "((Car)
vehicle).Color". That's my preference though - others might think "(Car)"
looks better. Let me preempt my fellow NGers by saying that you should only
optimize yada yada yada. I say, ya know what, I'm developing on mobile
devices running a 200MHz, I'm gonna use every little speed enhancement I can
get especially when it is for free.

Very important to note is that they are different as Jon pointed out, so
sometimes it makes sense to use one instead of the other.

Anyway, there you go.

Hilton
 
S

Sujeet

Hilton,
Why is 'as' much faster that (Card), except in cases where (Card) might
throw an exception?
Otherwise, I think in both cases the CLR needs to walk the same level of
object type structures and so should be have the same running time.

Sujeet
 
J

Jon Skeet [C# MVP]

I use "as" because it is much faster than the "(Card)" variety of casting.

No it's not. At least, it's not on the desktop CLR.

It's faster to use "as" and then a comparison with null than it is to
use "is" and then a direct cast, but if you're just casting then
they're about the same - with casting coming out slightly on top in
one microbenchmark I've just run:

using System;
using System.Diagnostics;

public class Test
{
static void Main(string[] args)
{
object o = "hello";
int iterations = int.Parse(args[0]);
int total = 0;
Stopwatch sw = Stopwatch.StartNew();
for (int i=0; i < iterations; i++)
{
total += Cast(o).Length;
}
sw.Stop();
Console.WriteLine(total);
Console.WriteLine(sw.ElapsedMilliseconds);
}

static string Cast(object o)
{
return (string)o;
}

static string As(object o)
{
return o as string;
}
}

Change the call appropriately to test the different methods, and then
change the calling code to have the cast/as directly in the loop:

Running with 2,000,000,000 iterations, 3 runs each
As: 9595, 9567m 9565
Cast: 7180, 7199, 7216
As inline: 6355, 6393, 6595
Cast inline: 4777, 4807, 4812

Normal caveats about microbenchmarks apply, but I'd be interested to
see the results of you running the above tests on the CF. It certainly
looks to me like "as" is marginally slower, and certainly far from
"much faster".
Also, I think "(vehicle as Car).Color" looks a lot cleaner than "((Car)
vehicle).Color". That's my preference though - others might think "(Car)"
looks better. Let me preempt my fellow NGers by saying that you should only
optimize yada yada yada. I say, ya know what, I'm developing on mobile
devices running a 200MHz, I'm gonna use every little speed enhancement I can
get especially when it is for free.

Except it's not for free anyway - it means you get a
NullReferenceException instead of an InvalidCastException if
anything's wrong, and that's if you're lucky and use the referece
immediately. Using "as" when you really expect a cast to work can
obscure the origin of a bug.
Very important to note is that they are different as Jon pointed out, so
sometimes it makes sense to use one instead of the other.

One thing I didn't mention about the difference between the two is
nulls. If you cast and end up with null, you know that the source was
null. If you use "as" and end up with null, it's either because the
original reference was null *or* because it referred to an object of
the wrong type.

Jon
 
H

Hilton

Jon said:
Normal caveats about microbenchmarks apply, but I'd be interested to
see the results of you running the above tests on the CF. It certainly
looks to me like "as" is marginally slower, and certainly far from
"much faster".

I remember doing this test after seeing an article in CodeProject and my
results definitely showed that "as" was much faster - see the CodeProject
article: http://69.10.233.10/KB/cs/csharpcasts.aspx

To verify before I posted my earlier post today, I ran some code on my
desktop. The two inner loops looked like:

{
Blob b = (Blob) o;
}

and

{
Blob b = o as Blob;
}

....and the second version was indeed *much* faster than the first, so I was
confident posting that it was much faster. Only when I went back and
thought about it, I realized that the compiler could safely remove "o as
Blob", but *not* "(Blob) o" - right? So, I was, in effect, testing compiler
optimization which I am glad to say is alive and well. OK, perhaps I had
originally tested in on CF 1. So I wrote a quick app, ran it on my X51 and
the two versions ran in exactly the same time (give or take). The
CodeProject guy never posted any code, perhaps he made the same "compiler
optimization" error as I did.

Interestingly though, the two are very different beasts, used in different
circumstances, and definitely help to self-document if used correctly. The
cool thing though is that C# has both of these tools.

OK, I was wrong - sorry about that and thanks to Jon for correcting me.

Hilton
 
J

Jon Skeet [C# MVP]

I remember doing this test after seeing an article in CodeProject and my
results definitely showed that "as" was much faster - see the CodeProject
article:http://69.10.233.10/KB/cs/csharpcasts.aspx

<snip>

Interestingly, there's a post there saying that the performance of
direct casting was significantly improved for .NET 2.0 - so that's
probably where the confusion comes from.

Now, for your particular case it's worth investigating whether that's
also true on the CF - or rather, for the particular version of the CF
you're using. I wouldn't be surprised if the performance
characteristics were quite different there.

Interestingly though, the two are very different beasts, used in different
circumstances, and definitely help to self-document if used correctly. The
cool thing though is that C# has both of these tools.

Yes, it's definitely nice to have both. I do wonder whether "as" might
not be better as a slightly different language construct though -
something to do:

Foo x = y as Foo;
if (x != null)
{
...
}

in one block, e.g.:

when (Foo x = y)
{
...
}

I'm not sure though. (And I certainly wouldn't want to introduce it
now - just an idea of what might have been nice at 1.0.)

Jon
 
H

Hilton

Jon said:
Interestingly, there's a post there saying that the performance of
direct casting was significantly improved for .NET 2.0 - so that's
probably where the confusion comes from.

Here are the CF numbers:

Running 1.0.4292.0:

33:13 <--start time
33:39 <--after () cast, before "as"
34:05 <--end time

i.e. both took 26 seconds


Running 2.0.7045.0:

35:04
35:11
35:18

i.e. both took 7 seconds - nice!

My app runs a lot zippier on CF2 than CF1. I don't recall noticing a huge
increase from 2.0 to 3.5, I'd need to do some timing, but 1.0 to 2.0 was
very noticable. Thank you Microsoft.

Hilton
 
J

Jon Skeet [C# MVP]

Here are the CF numbers:

Running 1.0.4292.0: <snip>
i.e. both took 26 seconds

Running 2.0.7045.0: <snip>
i.e. both took 7 seconds - nice!

Wow - that's impressive :)
My app runs a lot zippier on CF2 than CF1. I don't recall noticing a huge
increase from 2.0 to 3.5, I'd need to do some timing, but 1.0 to 2.0 was
very noticable. Thank you Microsoft.

I wouldn't expect to see much between 2 and 3.5 if it's like the
desktop - i.e. no hugely significant CLR changes, just some BCL
changes and the additional stuff for LINQ etc. (There *are* some CLR
changes, but nothing on the order of 1 to 2.)

I seem to remember that reflection was one of the biggest winners when
it came to performance improvements from 1 to 2. Possibly delegate
invocations as well, but I'm less confident on that front.

Jon
 
J

Jon Skeet [C# MVP]

Here you are talking about CF what is that?

The Compact Framework - a version of .NET used for mobile devices like
PDAs.

(There's also the .NET Micro Edition, but I don't know how widely used
that is.)

Jon
 
M

Marc Gravell

The Compact Framework - a version of .NET used for mobile devices like
(There's also the .NET Micro Edition, but I don't know how widely used
that is.)

And the Silverlight framework; to be honest I haven't done enough
digging to know if this re-uses any of the others, or is just a
completely different architecture, but it is yet another .NET host ;-p

[out of interest, if anybody else knows... ?]

Marc
 
C

Cor Ligthert [MVP]

Jon,
Using a direct cast also allows for user-defined conversions etc.
Are you sure of this, I thought that it was the opposite

(As that is the way the DirectCast in VB for Net is working oposite the
CType (Convert or Cast Type), which are in my idea a little bit equivalent
to this. The DirectCast should be sometimes slightly quicker, those things I
never measure)

Cor
 
M

Marc Gravell

Using a direct cast also allows for user-defined conversions etc.
Are you sure of this, I thought that it was the opposite

cast will use defined implicit/explicit operators; "as" doesn't - you
get this instead:

"Cannot convert type 'Bar' to 'Foo' via a reference conversion, boxing
conversion, unboxing conversion, wrapping conversion, or null type
conversion"

In the ECMA spec, sections 14.6.6 and 14.9.11

Marc
 
J

Jon Skeet [C# MVP]

Are you sure of this, I thought that it was the opposite

Absolutely positive. Here's proof:
XElement has an explicit user-defined conversion operator to string.

using System.Xml.Linq;

public class Test
{
static void Main()
{
XElement element = new XElement("name", "text");

// Direct cast is fine
string text = (string) element;

// Doesn't compile - no reference conversion, boxing conversion,
// unboxing conversion, wrapping conversion or null type
conversion
// available
string text2 = element as XElement;
}
}

Jon
 
C

Cor Ligthert[MVP]

(string) element

Jon,

Probably just the terminology as I thought to see

(string) X, is in my ide more or less the same like CType(X,string)
while I see
X as string, the same as DirectCast(X, string)

You use the terminonlogy direct cast for (string)X , while I use for direct
cast for: cast but exclude any indirect conversion.

Strange is maybe that I use in VB seldom CType but try to do always
DirectCast, while in C# I seldom use "as", maybe I should use the latter
more. (Can be that it is because there are in VB standard so many more
optimized conversion functions).

Cor
 
J

Jon Skeet [C# MVP]

Probably just the terminology as I thought to see

(string) X, is in my ide more or less the same like CType(X,string)
while I see X as string, the same as DirectCast(X, string)

As I wrote to Scott, (string) X is the equivalent of DirectCast in
many situations, but that doesn't cover everything. "as" is more like
TryCast in VB.
You use the terminonlogy direct cast for (string)X , while I use for direct
cast for: cast but exclude any indirect conversion.

But in C# there's just the one syntax which does the three, and that's
what we've been talking about as a "direct cast" just to distinguish
it from using "as". There's no cast operator which does the same
conversions as "as" but throwing an exception if it fails. However,
the decision it use the direct cast syntax doesn't mean that the
nature of the conversion is decided at execution time - the compiler
works out what it's going to do, and emits the appropriate code.
Strange is maybe that I use in VB seldom CType but try to do always
DirectCast, while in C# I seldom use "as", maybe I should use the latter
more.

I doubt it - only if you're testing whether or not a conversion will
work and then acting on that.
(Can be that it is because there are in VB standard so many more
optimized conversion functions).

What exactly do you mean? Any time there's a conversion available as
either an inheritance-style conversion or a user-defined conversion
operator, C# will happily use those and they're likely to be
optimised. For less obvious conversions (such as string to int, which
involves parsing, cultures etc) you just call the appropriate method -
I can't see how VB would be more "optimized" there.

Jon
 
C

Cor Ligthert [MVP]

Jon,
snip
What exactly do you mean? Any time there's a conversion available as
either an inheritance-style conversion or a user-defined conversion
operator, C# will happily use those and they're likely to be
optimised.
snip

And it does not need to do any instructions to find the best way, amazing!

:)

Cor
 
J

Jon Skeet [C# MVP]

snip> What exactly do you mean? Any time there's a conversion available as

snip

And it does not need to do any instructions to find the best way, amazing!

I don't see what's surprising here. You can't write a user-defined
operator to perform a conversion which is alreay available, so there's
no ambiguity there. If you *are* writing a user-defined conversion
operator, you're likely to do that in a sensible way.

It's not that the compiler is optimising anything here - it's just
using one piece of syntax for three different things (unboxing, user-
defined conversion, inheritance-style casting). It can easily work out
which to do at compile time. What are you surprised by?

Jon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top