Problem
I’ve discovered that I do a lot of casting or object type conversion when programming interfaces.
Is there a distinction between these two conversion methods? Is there a cost difference or how does this affect my program if this is the case?
public interface IMyInterface
{
void AMethod();
}
public class MyClass : IMyInterface
{
public void AMethod()
{
//Do work
}
// Other helper methods....
}
public class Implementation
{
IMyInterface _MyObj;
MyClass _myCls1;
MyClass _myCls2;
public Implementation()
{
_MyObj = new MyClass();
// What is the difference here:
_myCls1 = (MyClass)_MyObj;
_myCls2 = (_MyObj as MyClass);
}
}
Also, what is the preferred way “in general”?
Asked by Frank V
Solution #1
The answer below the line was written in 2008.
Pattern matching was introduced in C# 7, and it has basically superseded the as operator, as you can now write:
if (randomObject is TargetType tt)
{
// Use tt here
}
After this, tt is still in scope, but it isn’t definitively assigned. (Within the if body, it is clearly allocated.) That can be a little inconvenient in some cases, so if you truly want to introduce the fewest number of variables possible in every scope, you might want to stick with is followed by a cast.
I don’t believe any of the previous responses (at the time of writing this answer!) have adequately defined when each is appropriate to use.
EDIT: Except for the value type situation, where I’ve highlighted that unpacking to a nullable value type is actually slower – but consistent – none of the above mentions performance.
According to naasking’s response, with recent JITs, is-and-cast or is-and-as are both as fast as as-and-null-check, as proved by the code below:
using System;
using System.Diagnostics;
using System.Linq;
class Test
{
const int Size = 30000000;
static void Main()
{
object[] values = new object[Size];
for (int i = 0; i < Size - 2; i += 3)
{
values[i] = null;
values[i + 1] = "x";
values[i + 2] = new object();
}
FindLengthWithIsAndCast(values);
FindLengthWithIsAndAs(values);
FindLengthWithAsAndNullCheck(values);
}
static void FindLengthWithIsAndCast(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = (string) o;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and Cast: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithIsAndAs(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
if (o is string)
{
string a = o as string;
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("Is and As: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
static void FindLengthWithAsAndNullCheck(object[] values)
{
Stopwatch sw = Stopwatch.StartNew();
int len = 0;
foreach (object o in values)
{
string a = o as string;
if (a != null)
{
len += a.Length;
}
}
sw.Stop();
Console.WriteLine("As and null check: {0} : {1}", len,
(long)sw.ElapsedMilliseconds);
}
}
These all take roughly 60 milliseconds to complete on my pc. Two things to keep in mind:
So don’t be concerned about the performance. Let’s focus on accuracy and consistency.
When dealing with variables, I believe that is-and-cast (or is-and-as) are both hazardous since the type of the value it refers to may change owing to another thread between the test and the cast. That would be an uncommon occurrence, but I’d rather have a standard that I can follow every time.
I also believe that the as-then-null-check allows for a more effective separation of concerns. We have one sentence that tries to convert something, and another one that uses the result. The is-and-cast or is-and-as function conducts a test before attempting to convert the value again.
Would anyone ever write, to put it another way:
int value;
if (int.TryParse(text, out value))
{
value = int.Parse(text);
// Use value
}
That’s essentially what is-and-cast is doing, albeit in a much more cost-effective manner.
Answered by Jon Skeet
Solution #2
“as” will return NULL if not possible to cast.
casting before will raise an exception.
Raising an exception is usually more time consuming for the performance.
Answered by Patrick Desjardins
Solution #3
Another response, this time with some IL comparisons. Consider the following scenario:
public class MyClass
{
public static void Main()
{
// Call the 2 methods
}
public void DirectCast(Object obj)
{
if ( obj is MyClass)
{
MyClass myclass = (MyClass) obj;
Console.WriteLine(obj);
}
}
public void UsesAs(object obj)
{
MyClass myclass = obj as MyClass;
if (myclass != null)
{
Console.WriteLine(obj);
}
}
}
Take a look at the IL that each method generates. Even if you don’t understand the op codes, there is one significant difference: in the DirectCast method, isinst is called first, followed by castclass. So, instead of one call, there are two.
.method public hidebysig instance void DirectCast(object obj) cil managed
{
// Code size 22 (0x16)
.maxstack 8
IL_0000: ldarg.1
IL_0001: isinst MyClass
IL_0006: brfalse.s IL_0015
IL_0008: ldarg.1
IL_0009: castclass MyClass
IL_000e: pop
IL_000f: ldarg.1
IL_0010: call void [mscorlib]System.Console::WriteLine(object)
IL_0015: ret
} // end of method MyClass::DirectCast
.method public hidebysig instance void UsesAs(object obj) cil managed
{
// Code size 17 (0x11)
.maxstack 1
.locals init (class MyClass V_0)
IL_0000: ldarg.1
IL_0001: isinst MyClass
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0010
IL_000a: ldarg.1
IL_000b: call void [mscorlib]System.Console::WriteLine(object)
IL_0010: ret
} // end of method MyClass::UsesAs
The castclass vs. the isinst keyword
This blog post does a good job of contrasting the two approaches. His summary is as follows:
I always use As since it’s simple to read and the.NET development team recommends it (or Jeffrey Richter anyway)
Answered by Chris S
Solution #4
One of the more subtle differences between the two is that when a cast operator is involved, the “as” keyword cannot be used for casting:
public class Foo
{
public string Value;
public static explicit operator string(Foo f)
{
return f.Value;
}
}
public class Example
{
public void Convert()
{
var f = new Foo();
f.Value = "abc";
string cast = (string)f;
string tryCast = f as string;
}
}
Because the “as” keywords do not take cast operators into account, this will not compile on the last line (although I believe it did in prior versions). However, the line string cast = (string)f; works perfectly.
Answered by Patrik Hägne
Solution #5
If it can’t make the conversion, it never throws an exception, instead returning null (as operates on reference types only). As a result, using as is essentially the same as using
_myCls2 = _myObj is MyClass ? (MyClass)_myObj : null;
When no conversion is possible, however, C-style casts throw an exception.
Answered by Anton Gogolev
Post is based on https://stackoverflow.com/questions/496096/casting-vs-using-the-as-keyword-in-the-clr