"is" vs. "as"

  • Thread starter Thread starter Bobby C. Jones
  • Start date Start date
B

Bobby C. Jones

Is there an advantage to either of these methods, or is it simply a matter
of style? I used to use the "as" method, but later gave it up for the "is"
method as the intent of the code seems a little clearer to me. I now have
some projects coming up where the baseTypeCollection will contain 5k to 10k
objects that will have to be searched for various sub types of which I only
expect to find ~50 to ~100 matches. Thanks!

foreach (baseType in baseTypeCollection)
{
if (baseType is desiredSubType)
{
typeFound = (desiredSubType) baseType;
//do my thing with typeFound
}
}


foreach (baseType in baseTypeCollection)
{
typeFound = baseType as desiredSubType;
if (typeFound != null)
{
//do my thing with typeFound
}
}
 
Hi Bobby,

Bobby C. Jones said:
Is there an advantage to either of these methods, or is it simply a matter
of style? I used to use the "as" method, but later gave it up for the "is"
method as the intent of the code seems a little clearer to me. I now have
some projects coming up where the baseTypeCollection will contain 5k to 10k
objects that will have to be searched for various sub types of which I only
expect to find ~50 to ~100 matches. Thanks!

foreach (baseType in baseTypeCollection)
{
if (baseType is desiredSubType)
{
typeFound = (desiredSubType) baseType;
//do my thing with typeFound
}
}


foreach (baseType in baseTypeCollection)
{
typeFound = baseType as desiredSubType;
if (typeFound != null)
{
//do my thing with typeFound
}
}

Using "as" is more performat than using "is" because there is only one
cast instead of two. In most scenarios I would say the difference is
negligable, but given your scenario, you may just notice a difference.

Regards,
Dan
 
Bobby,

In a previous post, Greg Ewing found that when doing comparisons with
is, it is about 10X faster then checking using "as" or doing an equality
operation with GetType/typeof.

However, for what you are doing, I think that you can go one step
further. I think that what you should do is in your collection, have a
hashtable. This hashtable should be keyed on the type of the items that are
kept in the collection. The values in the hashtable are collections of the
items of that type.

So, when you add an element, you would check the type. Then look in the
hashtable against that type. Get the collection of items of that type, and
add a reference to the new value in your collection there. Also, make sure
to do the same when deleting.

Then, when you want to query for the elements that are of a particular
type, you don't have to enumerate through such a large number of items.
Granted, the insert and deletes are going to be a little slower, but
overall, if you do this kind of select often, it should increase the overall
efficiency of the class.

Hope this helps.
 
Thanks Nicholas and everyone! This is some great info to know.

The collection is actually one that I am receiving from a COM server, not
one that I'm creating myself. I had actually considered something like what
you are suggesting here Nicholas as the COM server does provide event
notification when objects are added and removed. But I wanted to wait and
see if iterating the entire collection will become a bottleneck or not
before adding the code to optimize the searches. This could be feasible and
not to damaging to my app's memory footprint because I won't need to search
for all of the subtypes, only a handful. I could just cache those types, or
even dynamically cache them as needed...Hmm...thanks for helping to get the
brain juices flowing!
 
You cant have duplicates in a Hashtable so how do you propose keying on
Type? Only one type alowed at any one time?
 
Nicholas Paldino said:
In a previous post, Greg Ewing found that when doing comparisons with
is, it is about 10X faster then checking using "as" or doing an equality
operation with GetType/typeof.

No - he found it was ten times faster than doing an equality check with
GetType/typeof. as/is were roughly the same speed, I believe - that
would certainly make sense, although if you then need to cast, "as"
should be faster.
 
The original poster (OP) indicated that the collection was storing
references of a base type. He is searching for the derived types in the
collection. The hashtable is going to be keyed on the type of the value
that is stored in the collection (GetType returns the type of the instance,
not the type of the reference).

The hashtable would store another collection, which has the references
to the values that are in the main collection, and provides an easy way to
look them up based on the type.
 
Jon,

Nope, these are the numbers that he posted:

Using the is operator took 00:00:00.0000240
Using GetType() took 00:00:00.0002363
Using the as operator took 00:00:00.0002394

The as operator was the slowest of them all.
 
Nicholas Paldino said:
Nope, these are the numbers that he posted:

Using the is operator took 00:00:00.0000240
Using GetType() took 00:00:00.0002363
Using the as operator took 00:00:00.0002394

The as operator was the slowest of them all.

Oops. In that case, I'm afraid I question the testing methodology *or*
Whidbey has *completely* different behaviour to v1.1, which it may do
for the beta, of course.

(Note that the code originally posted didn't even test it 1000 times,
so I'm assuming that wasn't the code that was actually run.)

Here's code that can run under .NET 1.1, and then the results:

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

class Stopwatch
{
DateTime start;
DateTime stop;

internal TimeSpan Elapsed
{
get
{
return stop-start;
}
}

internal void Start()
{
start = DateTime.Now;
}

internal void Stop()
{
stop = DateTime.Now;
}
}

class Class1
{

const int Iterations = 100000000;

static void Main(string[] args)
{
Control ctl = new DropDownList();
Stopwatch sw = new Stopwatch();
int j = 0;
sw.Start();
for (int i = 0; i < Iterations; i++)
{
if (ctl is System.Web.UI.WebControls.DropDownList)
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}
sw.Stop();
Console.WriteLine("Using the is operator took " + sw.Elapsed);

sw.Start();
for (int i = 0; i < Iterations; i++)
{
if (ctl.GetType() == typeof
System.Web.UI.WebControls.DropDownList))
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}

sw.Stop();
Console.WriteLine("Using GetType() took " + sw.Elapsed);

sw.Start();
for (int i=0; i < Iterations; i++)
{
if ((ctl as System.Web.UI.WebControls.DropDownList)
!= null)
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}
sw.Stop();
Console.WriteLine("Using the as operator took " + sw.Elapsed);
Console.Read();
}
}

My results:
Using the is operator took 00:00:00.7031250
Using GetType() took 00:00:04.9531250
Using the as operator took 00:00:00.5000000

Looking at the IL for the as/is cases, they look identical to me (I
suspect the variance in time shown above is due to JITting through the
as case, but I haven't investigated further). As I understand it,

if (x is Y)

is always transformed into exactly the same code as

if ((x as Y) != null)


When you then cast, it's cheaper to just use "as" a single time, as
that only requires the check to be performed once.
 
Jon Skeet said:
Looking at the IL for the as/is cases, they look identical to me (I
suspect the variance in time shown above is due to JITting through the
as case, but I haven't investigated further). As I understand it,

I also get similar performance for 'as' and 'is' on framework 1.0.
if (x is Y)
is always transformed into exactly the same code as
if ((x as Y) != null)

On the contrary, if you look at the IL you see that 'as' is converted to an
'is'. This makes sense because 'as' cannot be used for value types, while
'is' can.

Regards,
Pieter Philippaerts
Managed SSL/TLS: http://www.mentalis.org/go.php?sl
 
Dont use DateTime

Wrap QueryPerformanceCounter and QueryPerformanceFrequency native API



Jon Skeet said:
Nicholas Paldino said:
Nope, these are the numbers that he posted:

Using the is operator took 00:00:00.0000240
Using GetType() took 00:00:00.0002363
Using the as operator took 00:00:00.0002394

The as operator was the slowest of them all.

Oops. In that case, I'm afraid I question the testing methodology *or*
Whidbey has *completely* different behaviour to v1.1, which it may do
for the beta, of course.

(Note that the code originally posted didn't even test it 1000 times,
so I'm assuming that wasn't the code that was actually run.)

Here's code that can run under .NET 1.1, and then the results:

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

class Stopwatch
{
DateTime start;
DateTime stop;

internal TimeSpan Elapsed
{
get
{
return stop-start;
}
}

internal void Start()
{
start = DateTime.Now;
}

internal void Stop()
{
stop = DateTime.Now;
}
}

class Class1
{

const int Iterations = 100000000;

static void Main(string[] args)
{
Control ctl = new DropDownList();
Stopwatch sw = new Stopwatch();
int j = 0;
sw.Start();
for (int i = 0; i < Iterations; i++)
{
if (ctl is System.Web.UI.WebControls.DropDownList)
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}
sw.Stop();
Console.WriteLine("Using the is operator took " + sw.Elapsed);

sw.Start();
for (int i = 0; i < Iterations; i++)
{
if (ctl.GetType() == typeof
System.Web.UI.WebControls.DropDownList))
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}

sw.Stop();
Console.WriteLine("Using GetType() took " + sw.Elapsed);

sw.Start();
for (int i=0; i < Iterations; i++)
{
if ((ctl as System.Web.UI.WebControls.DropDownList)
!= null)
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}
sw.Stop();
Console.WriteLine("Using the as operator took " + sw.Elapsed);
Console.Read();
}
}

My results:
Using the is operator took 00:00:00.7031250
Using GetType() took 00:00:04.9531250
Using the as operator took 00:00:00.5000000

Looking at the IL for the as/is cases, they look identical to me (I
suspect the variance in time shown above is due to JITting through the
as case, but I haven't investigated further). As I understand it,

if (x is Y)

is always transformed into exactly the same code as

if ((x as Y) != null)


When you then cast, it's cheaper to just use "as" a single time, as
that only requires the check to be performed once.
 
news.microsoft.com said:
Dont use DateTime

If you're using enough iterations, DateTime is fine. On my computer the
different loops take between 1.3s and 11s. If you know that DateTime has a
resolution of around 10ms, I'm sure that you'll agree it's good enough.

Regards,
Pieter Philippaerts
Managed SSL/TLS: http://www.mentalis.org/go.php?sl
 
Jon,

FYI these are the whidbey results on a 1Ghz PIII

Using the is operator took 00:00:01.1315933
Using GetType() took 00:00:02.6637506
Using the as operator took 00:00:01.0214382

Good to see GetType got a los faster (as a lot of others :-))
Willy.

Jon Skeet said:
Nicholas Paldino said:
Nope, these are the numbers that he posted:

Using the is operator took 00:00:00.0000240
Using GetType() took 00:00:00.0002363
Using the as operator took 00:00:00.0002394

The as operator was the slowest of them all.

Oops. In that case, I'm afraid I question the testing methodology *or*
Whidbey has *completely* different behaviour to v1.1, which it may do
for the beta, of course.

(Note that the code originally posted didn't even test it 1000 times,
so I'm assuming that wasn't the code that was actually run.)

Here's code that can run under .NET 1.1, and then the results:

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

class Stopwatch
{
DateTime start;
DateTime stop;

internal TimeSpan Elapsed
{
get
{
return stop-start;
}
}

internal void Start()
{
start = DateTime.Now;
}

internal void Stop()
{
stop = DateTime.Now;
}
}

class Class1
{

const int Iterations = 100000000;

static void Main(string[] args)
{
Control ctl = new DropDownList();
Stopwatch sw = new Stopwatch();
int j = 0;
sw.Start();
for (int i = 0; i < Iterations; i++)
{
if (ctl is System.Web.UI.WebControls.DropDownList)
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}
sw.Stop();
Console.WriteLine("Using the is operator took " + sw.Elapsed);

sw.Start();
for (int i = 0; i < Iterations; i++)
{
if (ctl.GetType() == typeof
System.Web.UI.WebControls.DropDownList))
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}

sw.Stop();
Console.WriteLine("Using GetType() took " + sw.Elapsed);

sw.Start();
for (int i=0; i < Iterations; i++)
{
if ((ctl as System.Web.UI.WebControls.DropDownList)
!= null)
{
j++;
//Console.WriteLine("ctl is a DropDownList");
}
else
{
j++;
//Console.WriteLine("ctl is NOT a DropDownList");
}
}
sw.Stop();
Console.WriteLine("Using the as operator took " + sw.Elapsed);
Console.Read();
}
}

My results:
Using the is operator took 00:00:00.7031250
Using GetType() took 00:00:04.9531250
Using the as operator took 00:00:00.5000000

Looking at the IL for the as/is cases, they look identical to me (I
suspect the variance in time shown above is due to JITting through the
as case, but I haven't investigated further). As I understand it,

if (x is Y)

is always transformed into exactly the same code as

if ((x as Y) != null)


When you then cast, it's cheaper to just use "as" a single time, as
that only requires the check to be performed once.
 
Pieter Philippaerts said:
I also get similar performance for 'as' and 'is' on framework 1.0.
Good.


On the contrary, if you look at the IL you see that 'as' is converted to an
'is'. This makes sense because 'as' cannot be used for value types, while
'is' can.

No - "isinst" is pretty badly named, but it basically does *exactly*
what the "as" operator in C# does.

From the ECMA spec:

<quote>
The isinst instruction tests whether obj (type O) is an instance of
class. Class is a metadata token (a typeref or typedef see Partition
II) indicating the desired class. If the class of the object on the top
of the stack implements class (if class is an interface) or is a
subclass of class (if class is a regular class), then it is cast to
the type class and the result is pushed on the stack, exactly as though
castclass had been called. Otherwise null is pushed on the stack. If
obj is null, isinst returns null.
</quote>

If that's not pretty much the definition of what the "as" operator
does, I don't know what is :)
 
In a previous post, Greg Ewing found that when doing comparisons with
is, it is about 10X faster then checking using "as" or doing an equality
operation with GetType/typeof.

That doesn't seem to be correct???

--


-----------
Got TidBits?
Get it here: www.networkip.net/tidbits
Nicholas Paldino said:
Bobby,

In a previous post, Greg Ewing found that when doing comparisons with
is, it is about 10X faster then checking using "as" or doing an equality
operation with GetType/typeof.

However, for what you are doing, I think that you can go one step
further. I think that what you should do is in your collection, have a
hashtable. This hashtable should be keyed on the type of the items that are
kept in the collection. The values in the hashtable are collections of the
items of that type.

So, when you add an element, you would check the type. Then look in the
hashtable against that type. Get the collection of items of that type, and
add a reference to the new value in your collection there. Also, make sure
to do the same when deleting.

Then, when you want to query for the elements that are of a particular
type, you don't have to enumerate through such a large number of items.
Granted, the insert and deletes are going to be a little slower, but
overall, if you do this kind of select often, it should increase the overall
efficiency of the class.

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Bobby C. Jones said:
Is there an advantage to either of these methods, or is it simply a matter
of style? I used to use the "as" method, but later gave it up for the "is"
method as the intent of the code seems a little clearer to me. I now have
some projects coming up where the baseTypeCollection will contain 5k to 10k
objects that will have to be searched for various sub types of which I only
expect to find ~50 to ~100 matches. Thanks!

foreach (baseType in baseTypeCollection)
{
if (baseType is desiredSubType)
{
typeFound = (desiredSubType) baseType;
//do my thing with typeFound
}
}


foreach (baseType in baseTypeCollection)
{
typeFound = baseType as desiredSubType;
if (typeFound != null)
{
//do my thing with typeFound
}
}
 
Alvin Bruney said:
That doesn't seem to be correct???

It's correct that Greg posted it (I blundered there) - but the results
aren't consistent with behaviour on the shipping framework, at least,
or Willy's installation of Whidbey.
 
Back
Top