Ok so I was chatting about this with someone and decided to test this more. As far as I can tell, the performance of as and is are both very good, compared to testing your own member or function to store type information.
I used Stopwatch, which I just learned may not be the most reliable approach, so I also tried UtcNow. Later, I also tried Processor time approach which seems similar to UtcNow including unpredictable create times. I also tried making the base class non-abstract with no virtuals but it didn't seem to have a significant effect.
I ran this on a Quad Q6600 with 16GB RAM. Even with 50mil iterations, the numbers still bounce around +/- 50 or so millisec so I wouldn't read too much into the minor differences.
It was interesting to see that x64 created faster but executed as/is slower than x86
x64 Release Mode:
Stopwatch:
As: 561ms
Is: 597ms
Base property: 539ms
Base field: 555ms
Base RO field: 552ms
Virtual GetEnumType() test: 556ms
Virtual IsB() test: 588ms
Create Time : 10416ms
UtcNow:
As: 499ms
Is: 532ms
Base property: 479ms
Base field: 502ms
Base RO field: 491ms
Virtual GetEnumType(): 502ms
Virtual bool IsB(): 522ms
Create Time : 285ms (This number seems unreliable with UtcNow. I also get 109ms and 806ms.)
x86 Release Mode:
Stopwatch:
As: 391ms
Is: 423ms
Base property: 369ms
Base field: 321ms
Base RO field: 339ms
Virtual GetEnumType() test: 361ms
Virtual IsB() test: 365ms
Create Time : 14106ms
UtcNow:
As: 348ms
Is: 375ms
Base property: 329ms
Base field: 286ms
Base RO field: 309ms
Virtual GetEnumType(): 321ms
Virtual bool IsB(): 332ms
Create Time : 544ms (This number seems unreliable with UtcNow.)
Here's most of the code:
static readonly int iterations = 50000000;
void IsTest()
{
Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1;
MyBaseClass[] bases = new MyBaseClass[iterations];
bool[] results1 = new bool[iterations];
Stopwatch createTime = new Stopwatch();
createTime.Start();
DateTime createStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
if (i % 2 == 0) bases[i] = new MyClassA();
else bases[i] = new MyClassB();
}
DateTime createStop = DateTime.UtcNow;
createTime.Stop();
Stopwatch isTimer = new Stopwatch();
isTimer.Start();
DateTime isStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i] is MyClassB;
}
DateTime isStop = DateTime.UtcNow;
isTimer.Stop();
CheckResults(ref results1);
Stopwatch asTimer = new Stopwatch();
asTimer.Start();
DateTime asStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i] as MyClassB != null;
}
DateTime asStop = DateTime.UtcNow;
asTimer.Stop();
CheckResults(ref results1);
Stopwatch baseMemberTime = new Stopwatch();
baseMemberTime.Start();
DateTime baseStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].ClassType == MyBaseClass.ClassTypeEnum.B;
}
DateTime baseStop = DateTime.UtcNow;
baseMemberTime.Stop();
CheckResults(ref results1);
Stopwatch baseFieldTime = new Stopwatch();
baseFieldTime.Start();
DateTime baseFieldStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
}
DateTime baseFieldStop = DateTime.UtcNow;
baseFieldTime.Stop();
CheckResults(ref results1);
Stopwatch baseROFieldTime = new Stopwatch();
baseROFieldTime.Start();
DateTime baseROFieldStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
}
DateTime baseROFieldStop = DateTime.UtcNow;
baseROFieldTime.Stop();
CheckResults(ref results1);
Stopwatch virtMethTime = new Stopwatch();
virtMethTime.Start();
DateTime virtStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].GetClassType() == MyBaseClass.ClassTypeEnum.B;
}
DateTime virtStop = DateTime.UtcNow;
virtMethTime.Stop();
CheckResults(ref results1);
Stopwatch virtMethBoolTime = new Stopwatch();
virtMethBoolTime.Start();
DateTime virtBoolStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].IsB();
}
DateTime virtBoolStop = DateTime.UtcNow;
virtMethBoolTime.Stop();
CheckResults(ref results1);
asdf.Text +=
"Stopwatch: " + Environment.NewLine
+ "As: " + asTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
+"Is: " + isTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
+ "Base property: " + baseMemberTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base field: " + baseFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base RO field: " + baseROFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType() test: " + virtMethTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual IsB() test: " + virtMethBoolTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Create Time : " + createTime.ElapsedMilliseconds + "ms" + Environment.NewLine + Environment.NewLine+"UtcNow: " + Environment.NewLine + "As: " + (asStop - asStart).Milliseconds + "ms" + Environment.NewLine + "Is: " + (isStop - isStart).Milliseconds + "ms" + Environment.NewLine + "Base property: " + (baseStop - baseStart).Milliseconds + "ms" + Environment.NewLine + "Base field: " + (baseFieldStop - baseFieldStart).Milliseconds + "ms" + Environment.NewLine + "Base RO field: " + (baseROFieldStop - baseROFieldStart).Milliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType(): " + (virtStop - virtStart).Milliseconds + "ms" + Environment.NewLine + "Virtual bool IsB(): " + (virtBoolStop - virtBoolStart).Milliseconds + "ms" + Environment.NewLine + "Create Time : " + (createStop-createStart).Milliseconds + "ms" + Environment.NewLine;
}
}
abstract class MyBaseClass
{
public enum ClassTypeEnum { A, B }
public ClassTypeEnum ClassType { get; protected set; }
public ClassTypeEnum ClassTypeField;
public readonly ClassTypeEnum ClassTypeReadonlyField;
public abstract ClassTypeEnum GetClassType();
public abstract bool IsB();
protected MyBaseClass(ClassTypeEnum kind)
{
ClassTypeReadonlyField = kind;
}
}
class MyClassA : MyBaseClass
{
public override bool IsB() { return false; }
public override ClassTypeEnum GetClassType() { return ClassTypeEnum.A; }
public MyClassA() : base(MyBaseClass.ClassTypeEnum.A)
{
ClassType = MyBaseClass.ClassTypeEnum.A;
ClassTypeField = MyBaseClass.ClassTypeEnum.A;
}
}
class MyClassB : MyBaseClass
{
public override bool IsB() { return true; }
public override ClassTypeEnum GetClassType() { return ClassTypeEnum.B; }
public MyClassB() : base(MyBaseClass.ClassTypeEnum.B)
{
ClassType = MyBaseClass.ClassTypeEnum.B;
ClassTypeField = MyBaseClass.ClassTypeEnum.B;
}
}