Note: This is not a duplicate of "Use of IsAssignableFrom and “is” keyword in C#". That other question asks about
typeof(T).IsAssignableFrom(type))
, wheretype
is not anobject
but aType
.This seems trivial — I can hear you saying, "Just call
x.GetType()
!" — but due to the COM-related corner case mentioned below, that call causes problems, which is why I'm asking about the rewrite.
… or are there rare special cases where the two might give different results?
I stumbled upon a type check of the form:
typeof(TValue).IsAssignableFrom(value.GetType())
where TValue
is a generic type parameter (without any constraints) and value
is an object
.
I am not entirely sure whether it is safe to rewrite the above simply as:
value is TValue
To my current knowledge, the two tests are equivalent with the exception of COM objects. is
should trigger a proper QueryInterface
, while IsAssignableFrom
might get confused by the __ComObject
RCW wrapper type and report a false negative.
Are there any other differences between is
and the shown use of IsAssignableFrom
?
There are more cases where is
and IsAssignableFrom
returns different results, not just the one you mentioned for COM objects. For pair of array types with elements of type ElementType1 and ElementType2, where underlaying types of both element types are integer types of the same size but with opposite signedness, then
typeof(ElementType1[]).IsAssignableFrom(typeof(ElementType2[]))
returns true but
new ElementType2[0] is ElementType1[]
returns false
Specifically this includes arrays with these pairs of their element types:
byte / sbyte, short / ushort, int / uint, long / ulong
IntPtr / UIntPtr
any combination of enum type and either integer type or another enum type as long as underlaying types are of the same size
any combination of IntPtr / UIntPtr / int / uint in 32-bit process
any combination of IntPtr / UIntPtr / long / ulong in 64-bit process
This is due to differences in type system of C# and CLR as explained in
Different results of is
and IsAssignableFrom
in all cases mentioned above results from the fact that for new ElementType2[0] is ElementType1[]
C# compiler simply emits False
at compile-time (because it sees no way that for example int[] can be cast to uint[] as these are completly different types from C# perspective), completly omitting any runtime type checks. Fortunately casting array to object ((object)new ElementType2[0]) is ElementType1[]
forces compiler to emit isinst
IL instruction, which performs runtime type-check, that returns results consistent with IsAssignableFrom
. This is also true for cases where target type is generic parameter, because it's type is not known at compile-time and C# compiler must emit isinst
. So if you intend to replace IsAssignableFrom
by is
only in places where target type is generic parameter (as suggested in question title), I believe that these differences does not metter for you.
static void Main(string[] args)
{
int? bob = null;
Test(bob);
}
private static void Test<T>(T bob)
{
Console.WriteLine(bob is T);
Console.WriteLine(typeof(T).IsInstanceOfType(bob));
Console.WriteLine(typeof(T).IsAssignableFrom(bob.GetType()));
Console.ReadLine();
}
Is an example where they act slightly differently (since bob is null). https://stackoverflow.com/a/15853213/34092 may be of interest.
Other than that (and the other exceptions you mention) they do appear to be equivalent.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With