dkson: I tested both methods. Here’s what I found on my computer:
They are about the same in performance. In fact, the second method I found to be slightly slower. The reason (I believe) is the cost of the extra variable and initial cast. Of course, if you use enough casts you may get that performance cost back. It looks like you break even in terms of performance only after saving 20-30 casts.
Here are the results from the two most recent test runs:
TestMuliCast_3x: 00:00:00.5970000
TestSingleCast_3x: 00:00:00.6020000
TestMuliCast_30x: 00:00:06.0930000
TestSingleCast_30x: 00:00:06.0480000
TestMuliCast_3x: 00:00:00.6120000
TestSingleCast_3x: 00:00:00.6250000
TestMuliCast_30x: 00:00:06.5490000
TestSingleCast_30x: 00:00:06.4440000
I also tested the difference between castclass and isinst. Based on what I had read:
http://m3mia.blogspot.com/2007/11/comparing-isinst-to-castclass.html
http://www.codeproject.com/KB/cs/csharpcasts.aspx
http://discuss.joelonsoftware.com/default.asp?dotnet.12.635066.13
...I thought isinst would be faster than castclass even when there were no exceptions. However, after creating my own benchmarks, I found isinst to be slightly slower than castclass. Very Interesting. Here are my results:
TestEmptyLoop: 00:00:00.0870000
TestDCast_castclass: 00:00:00.2640000
TestDCast_isinst: 00:00:00.3780000
TestEmptyLoop: 00:00:00.0870000
TestDCast_castclass: 00:00:00.2600000
TestDCast_isinst: 00:00:00.3750000
So, Mr. Skeet, I stand corrected.
Environment:
Windows Vista
Maximum Core Speed 3.2Ghz
.NET Framework v2.0.50727
Here is the full source to the benchmarks that I created and ran:
(Makes use of Jon Skeets Microbenchmarking framework available here: http://www.yoda.arachsys.com/csharp/benchmark.html)
using System;
using System.Collections;
public class CastingBenchmark
{
static Int64 Iterations=100000000;
static Int64 TestWork = 0;
public static void Init(string[] args)
{
if (args.Length>0)
Iterations = Int64.Parse(args[0]);
}
public static void Reset()
{
TestWork = 0;
}
internal class BaseType { public void TestBaseMethod() { TestWork++; } }
internal class DerivedType : BaseType {
public void TestDerivedMethod() { TestWork++; }
public void TestDerivedMethod2() { TestWork++; }
public void TestDerivedMethod3() { TestWork++; }
}
[Benchmark]
public static void TestMuliCast_3x()
{
BaseType TestBaseType = new DerivedType();
for (int x = 0; x < Iterations; x++)
{
((DerivedType)TestBaseType).TestDerivedMethod();
((DerivedType)TestBaseType).TestDerivedMethod2();
((DerivedType)TestBaseType).TestDerivedMethod3();
}
}
[Benchmark]
public static void TestSingleCast_3x()
{
BaseType TestBaseType = new DerivedType();
for (int x = 0; x < Iterations; x++)
{
DerivedType TestDerivedType = (DerivedType)TestBaseType;
TestDerivedType.TestDerivedMethod();
TestDerivedType.TestDerivedMethod2();
TestDerivedType.TestDerivedMethod3();
}
}
[Benchmark]
public static void TestMuliCast_30x()
{
BaseType TestBaseType = new DerivedType();
for (int x = 0; x < Iterations; x++)
{
//Simulate 3 x 10 method calls while casting
for (int y = 0; y < 10; y++) {
((DerivedType)TestBaseType).TestDerivedMethod();
((DerivedType)TestBaseType).TestDerivedMethod2();
((DerivedType)TestBaseType).TestDerivedMethod3();
}
}
}
[Benchmark]
public static void TestSingleCast_30x()
{
BaseType TestBaseType = new DerivedType();
for (int x = 0; x < Iterations; x++)
{
DerivedType TestDerivedType = (DerivedType)TestBaseType;
//Simulate 3 x 10 method calls on already-cast object
for (int y = 0; y < 10; y++)
{
TestDerivedType.TestDerivedMethod();
TestDerivedType.TestDerivedMethod2();
TestDerivedType.TestDerivedMethod3();
}
}
}
[Benchmark]
public static void TestEmptyLoop()
{
for (int x = 0; x < Iterations; x++)
{
}
}
[Benchmark]
public static void TestDCast_castclass()
{
BaseType TestDerivedType = new DerivedType();
for (int x = 0; x < Iterations; x++)
{
((DerivedType)TestDerivedType).TestDerivedMethod();
}
}
[Benchmark]
public static void TestDCast_isinst()
{
BaseType TestDerivedType = new DerivedType();
for (int x = 0; x < Iterations; x++)
{
(TestDerivedType as DerivedType).TestDerivedMethod();
}
}
}
...And the resulting IL for isinst and castclass methods:
method public hidebysig static void TestDCast_isinst() cil managed
{
.custom instance void BenchmarkAttribute::.ctor()
.maxstack 2
.locals init (
[0] class CastingBenchmark/BaseType TestDerivedType,
[1] int32 x)
L_0000: newobj instance void CastingBenchmark/DerivedType::.ctor()
L_0005: stloc.0
L_0006: ldc.i4.0
L_0007: stloc.1
L_0008: br.s L_0019
L_000a: ldloc.0
L_000b: isinst CastingBenchmark/DerivedType
L_0010: callvirt instance void CastingBenchmark/DerivedType::TestDerivedMethod()
L_0015: ldloc.1
L_0016: ldc.i4.1
L_0017: add
L_0018: stloc.1
L_0019: ldloc.1
L_001a: conv.i8
L_001b: ldsfld int64 CastingBenchmark::Iterations
L_0020: blt.s L_000a
L_0022: ret
}
.method public hidebysig static void TestDCast_castclass() cil managed
{
.custom instance void BenchmarkAttribute::.ctor()
.maxstack 2
.locals init (
[0] class CastingBenchmark/BaseType TestDerivedType,
[1] int32 x)
L_0000: newobj instance void CastingBenchmark/DerivedType::.ctor()
L_0005: stloc.0
L_0006: ldc.i4.0
L_0007: stloc.1
L_0008: br.s L_0019
L_000a: ldloc.0
L_000b: castclass CastingBenchmark/DerivedType
L_0010: callvirt instance void CastingBenchmark/DerivedType::TestDerivedMethod()
L_0015: ldloc.1
L_0016: ldc.i4.1
L_0017: add
L_0018: stloc.1
L_0019: ldloc.1
L_001a: conv.i8
L_001b: ldsfld int64 CastingBenchmark::Iterations
L_0020: blt.s L_000a
L_0022: ret
}