Can `typeof (T) .IsAssignableFrom (x.GetType ())` be safely rewritten as `x is T`?

Note. This is not a duplicate of “Using IsAssignableFrom and” is a keyword in C # . This question asks the question typeof(T).IsAssignableFrom(type)) , where type not an object , but a type .

It seems trivial; I hear you say, "Just call x.GetType() !". - but due to the COM related related case mentioned below, this call causes problems, so I am asking about rewriting.

... or are there rare special cases where two can give different results?

I came across a form type check:

 typeof(TValue).IsAssignableFrom(value.GetType()) 

where TValue is a typical type parameter (without any restrictions), and value is object .

I am not quite sure whether it is possible to rewrite above, simply:

 value is TValue 

In my current knowledge, two tests are equivalent, with the exception of COM objects. is should call the correct QueryInterface , and IsAssignableFrom can get confused with the __ComObject RCW wrapper type and report an error.

Are there any other differences between is and the usage shown by IsAssignableFrom ?

+5
source share
2 answers

There are more cases where is and IsAssignableFrom return different results, and not just the ones you specified for COM objects. For a pair of array types with elements of type ElementType1 and ElementType2, where the underlying types of both types of elements are integer types of the same size, but with the opposite signature, then

typeof(ElementType1[]).IsAssignableFrom(typeof(ElementType2[])) returns true , but

new ElementType2[0] is ElementType1[] returns false

In particular, this includes arrays with these pairs of their element types:

  • byte / sbyte, short / ushort, int / uint, long / ulong

  • IntPtr / UIntPtr

  • any combination of an enum type and an integer type or another type of enum if the types of linings are the same size

  • any combination of IntPtr / UIntPtr / int / uint in a 32-bit process

  • any combination of IntPtr / UIntPtr / long / ulong in a 64-bit process

This is due to differences in the C # and CLR type system, as described in

The different results of is and IsAssignableFrom in all the cases mentioned above follow from the fact that for new ElementType2[0] is ElementType1[] , the C # compiler simply emits False at compile time (since it does not see the path, for example, for int [], be ported to uint [], since they are completely different types from the point of view of C #), completely excluding runtime type checks. Fortunately, casting an array to an object ((object)new ElementType2[0]) is ElementType1[] causes the compiler to generate the isinst IL command, which performs a runtime type check that returns results matching IsAssignableFrom . This is also true in cases where the target type is a common parameter, because its type is unknown at compile time, and the C # compiler must emit isinst . Therefore, if you intend to replace IsAssignableFrom with is only in those places where the target type is a common parameter (as suggested in the question heading), I believe that these differences are not for you.

+5
source
  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(); } 

An example is where they act slightly differently (since bob is null). fooobar.com/questions/170256 / ... may be of interest.

Apart from this (and the other exceptions you mention), they look like equivalent ones.

+4
source

Source: https://habr.com/ru/post/1268553/


All Articles