Reputation: 2415
I have next class:
public class A
{
public int MyProperty { get; set; }
}
And the following code in Main:
object obj = new A();
Stopwatch sw = Stopwatch.StartNew();
var res = obj as A;
if (res != null)
{
res.MyProperty = 10;
Console.WriteLine("obj is A (as)" + sw.Elapsed);
}
sw.Stop();
Stopwatch sw2 = Stopwatch.StartNew();
if (obj.GetType() == typeof(A))
{
A a = (A)obj;
a.MyProperty = 10;
Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);
}
sw2.Stop();
Stopwatch sw3 = Stopwatch.StartNew();
var isA = obj is A;
if (isA)
{
A a = (A)obj;
a.MyProperty = 19;
Console.WriteLine("obj is A (is)" + sw3.Elapsed);
}
sw3.Stop();
Console.ReadKey();
The results are:
obj is A (as) 00:00:00.0000589
obj is A (GetType) 00:00:00.0000024
obj is A (is) 00:00:00.0000006
The point is that operator 'is' works always faster than 'as'. Why 'as' is slower than 'is'? Even GetType() faster than 'as'. What stands for 'as' operator that cause such delays comparing to 'is' and GetType().
Upvotes: 0
Views: 129
Reputation: 109577
Your measurements are incorrect if they show that "as" is slower than "is".
The reason that can't be the case is that both the "as" and the "is" keywords generate an isinst
IL instruction, but the "is" instruction generates an additional check of the return value.
You do not need to perform any timings to determine this, because you can examine the generated IL code using reflector.
For example, this method:
static bool isTest(object value)
{
return value is Random;
}
Generates this IL:
.method private hidebysig static bool isTest(object 'value') cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: isinst [mscorlib]System.Random
L_0006: ldnull
L_0007: cgt.un
L_0009: ret
}
While this:
static object asTest(object value)
{
return value as Random;
}
Generates:
.method private hidebysig static object asTest(object 'value') cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: isinst [mscorlib]System.Random
L_0006: ret
}
The "is" keyword generates identical IL to the "as" keyword plus some additional instructions; therefore, "is" must be slower than "as" for this kind of usage.
Upvotes: 2
Reputation: 57202
I think it could be due to the fact that you are also setting MyProperty
, so the first time you do that the property setter is JIT-compiled.
Try running this code, with and without the commented lines, and check the difference:
object obj = new A();
// uncomment these lines and see the difference
// A tmp = new A();
// tmp.MyProperty = 100;
Stopwatch sw = Stopwatch.StartNew();
var res = obj as A;
if (res != null) {
res.MyProperty = 10;
}
sw.Stop();
Console.WriteLine("as : " + sw.Elapsed);
Stopwatch sw2 = Stopwatch.StartNew();
if (obj.GetType() == typeof(A)) {
A a = (A)obj;
a.MyProperty = 10;
}
sw2.Stop();
Console.WriteLine("GetType : " + sw2.Elapsed);
Stopwatch sw3 = Stopwatch.StartNew();
var isA = obj is A;
if (isA) {
A a = (A)obj;
a.MyProperty = 19;
}
sw3.Stop();
Console.WriteLine("is : " + sw3.Elapsed);
Upvotes: 1
Reputation: 700362
I would guess that it's the first Console.Write
that has to open a stream to the console, so that takes a lot more time.
Anyway, writing to the console takes so much more time than doing a cast, so you can't draw any conclusion at all about the castings from your test.
Doing the casts a billion times and don't write anything to the console for each cast, gives you a more reasonable result:
object obj = new A();
int iterations = 1000000000;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
var res = obj as A;
if (res != null) {
res.MyProperty = 10;
}
}
sw.Stop();
Console.WriteLine("obj is A (as)" + sw.Elapsed);
Stopwatch sw2 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
if (obj.GetType() == typeof(A)) {
A a = (A)obj;
a.MyProperty = 10;
}
}
sw2.Stop();
Console.WriteLine("obj is A (GetType)" + sw2.Elapsed);
Stopwatch sw3 = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
var isA = obj is A;
if (isA) {
A a = (A)obj;
a.MyProperty = 19;
}
}
sw3.Stop();
Console.WriteLine("obj is A (is)" + sw3.Elapsed);
Example output:
obj is A (as)00:00:00.3937249
obj is A (GetType)00:00:00.3452988
obj is A (is)00:00:01.0193541
Upvotes: 4