When programming interfaces, I’ve found I’m doing a lot of casting or object type conversion.
Is there a difference between these two methods of conversion? If so, is there a cost difference or how does this affect my program?
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 ‘in general’ the preferred method?
The answer below the line was written in 2008.
C# 7 introduced pattern matching, which has largely replaced the
asoperator, as you can now write:Note that
ttis still in scope after this, but not definitely assigned. (It is definitely assigned within theifbody.) That’s slightly annoying in some cases, so if you really care about introducing the smallest number of variables possible in every scope, you might still want to useisfollowed by a cast.I don’t think any of the answers so far (at the time of starting this answer!) have really explained where it’s worth using which.
Don’t do this:
Not only is this checking twice, but it may be checking different things, if
randomObjectis a field rather than a local variable. It’s possible for the ‘if’ to pass but then the cast to fail, if another thread changes the value ofrandomObjectbetween the two.If
randomObjectreally should be an instance ofTargetType, i.e. if it’s not, that means there’s a bug, then casting is the right solution. That throws an exception immediately, which means that no more work is done under incorrect assumptions, and the exception correctly shows the type of bug.If
randomObjectmight be an instance ofTargetTypeandTargetTypeis a reference type, then use code like this:If
randomObjectmight be an instance ofTargetTypeandTargetTypeis a value type, then we can’t useaswithTargetTypeitself, but we can use a nullable type:(Note: currently this is actually slower than is + cast. I think it’s more elegant and consistent, but there we go.)
If you really don’t need the converted value, but you just need to know whether it is an instance of TargetType, then the
isoperator is your friend. In this case it doesn’t matter whether TargetType is a reference type or a value type.There may be other cases involving generics where
isis useful (because you may not know whether T is a reference type or not, so you can’t use as) but they’re relatively obscure.I’ve almost certainly used
isfor the value type case before now, not having thought of using a nullable type andastogether 🙂EDIT: Note that none of the above talks about performance, other than the value type case, where I’ve noted that unboxing to a nullable value type is actually slower – but consistent.
As per naasking’s answer, is-and-cast or is-and-as are both as fast as as-and-null-check with modern JITs, as shown by the code below:
On my laptop, these all execute in about 60ms. Two things to note:
So let’s not worry about the performance. Let’s worry about correctness and consistency.
I maintain that is-and-cast (or is-and-as) are both unsafe when dealing with variables, as the type of the value it refers to may change due to another thread between the test and the cast. That would be a pretty rare situation – but I’d rather have a convention which I can use consistently.
I also maintain that the as-then-null-check gives a better separation of concerns. We have one statement which attempts a conversion, and then one statement which uses the result. The is-and-cast or is-and-as performs a test and then another attempt to convert the value.
To put it another way, would anyone ever write:
That’s sort of what is-and-cast is doing – although obviously in a rather cheaper way.