While reading C# code I found a rather curious snippet:
if( whatever is IDisposable) {
(whatever as IDisposable).Dispose();
}
I'd rather expect that being done either like this:
if( whatever is IDisposable) { //check
((IDisposable)whatever).Dispose(); //cast - won't fail
}
or like this:
IDisposable whateverDisposable = whatever as IDisposable;
if( whateverDisposable != null ) {
whateverDisposable.Dispose();
}
I mean as
is like a cast, but returns null
on failure. In the second snippet it can't fail (since there's a is
check before).
What's the point in writing code like in the first开发者_运维技巧 snippet instead of like in the second or in the third?
You are correct. The first snippet does not make sense.
From the alternatives, I would recommend the third one, since it is thread-safe in the sense that between the check and the method call, the object cannot be replaced by another one which does not implement the interface. Consider the following scenario with the second snippet:
if (whatever is IDisposable) { //check
// <-- here, some other thread changes the value of whatever
((IDisposable)whatever).Dispose(); // could fail
}
This can't happen with the third snippet.
Note: There are some subtle differences between a cast and as
with respect to user-defined conversions, but they do not apply in this case.
is
followed by as
is a pointless construct, as you suspect. Ultimately, if you're not sure what type something is or what interfaces it implements, there are two idioms -- one for reference types and one for value types.
When testing for reference types (or a boxed nullable), the last pattern you provide is correct -- as
followed by a null test. This will be the fastest approach as the runtime will only have to perform one type test.
When testing for boxed value types, the second pattern you provide is correct -- is
followed by a cast.
There is no point of using the as
after the is
is true, I suspect the code you read or the book is only showing you how to declare and use as
and is
. I would use it like you suggested in the second snippet through:
IDisposable whateverDisposable = whatever as IDisposable;
if( whateverDisposable != null)
{
whateverDisposable.Dispose();
}
Note also that in your first attempt "((IDisposable)whatever)
", you are casting the object which will take performance "time to cast", here you are casting twice the first time using as
and the second using the explicit cast, so the best way is to implement it using the second method "as IDisposable
then check if it null
".
Agree with the point, but if you really what it to look clean, how about an extension method like such:
public static void DisposeIfNecessary(this object obj)
{
if (obj != null && obj is IDisposable)
((IDisposable)obj).Dispose();
}
To answer your actual question: Experience and habit.
Before the inclusion of the as
keyword in .Net 2.0, the only way to safely determine if an object could be cast to a specific type/interface was with the is
keyword.
So, people got in the habit of using is
before attempting an explicit cast in order to avoid unnecessary exceptions. This led to the pattern you have second in your list of samples:
if(whatever is IDisposable) //check
{
((IDisposable)whatever).Dispose(); //cast - won't fail
}
Then, we got the safe-cast as
keyword. I would guess that most people, when they first started using as
, continued to use the familiar pattern, but replaced the direct cast with a safe-cast, and their pattern of choice morphed into your example 1. (I know I did this for awhile.)
if(whatever is IDisposable)
{
(whatever as IDisposable).Dispose();
}
Eventually, many or most either realized on their own, or were instructed by fxCop or CodeAnalysis that the 'proper' pattern is your example 3:
IDisposable whateverDisposable = whatever as IDisposable;
if(whateverDisposable != null )
{
whateverDisposable.Dispose();
}
Certainly there are some floating around who are at the example 1 stage still and haven't yet 'evolved' their pattern to your example 3 for some reason, or others who are still just using the good-old time-proven pattern of using is
followed by a direct cast.
You are right, The first code you posted is unnecessary. Either use the second if you want the scope of the casted variable to be inside the if
or use the third, if it doesn't matter, because it is doing the least amount of work.
Also, the last option obviously gives you an in-scope variable that you can reuse later without having to re-invoke a cast.
is
followed by as
is pointless, beyond being faster to type (because of the keystrokes required for the brackets and how helpful intellisense is with as
). Is is marginally slower than both your other examples because the runtime needs to do two type checks.
The CLR has some trickery for as
, and as such it should be faster than the is
followed by the cast (however, keep in mind that you can't use as
with value types).
精彩评论