Subclass test without regard to type argument
If you run:
type Car<'T> () = class end
type Mercedes () =
inherit Car<int> ()
let merc = Mercedes ()
and then run each of the following lines, you get the indicated results:
merc :? Mercedes // true
box merc :? Mercedes // true
merc :? Car<int> // error FS0193: types not compatible
box merc :? Car<int> // true
merc :? Car<_> // error FS0193: types not compatible
box merc :? Car<_> // false
- See the first four cases. Why do you need to box
merc
for the test againstCar<int>
, but not for the test againstMercedes
? - In the last two cases I'm trying to find something that'll return
true
becausemerc
is aCar
, without regard to the type argument. Is there such a thing?
2 answers
-
answered 2022-01-25 10:11
Tomas Petricek
To answer your second question, there is no built-in operator for testing whether a value inherits from a class regardless of a type argument. You can check this using reflection by getting the base type of
Mercedes
and comparing its generic type definition with the generic type definition ofCar<_>
:merc.GetType().BaseType.GetGenericTypeDefinition() = typedefof<Car<_>>
In practice, it may be much easier to introduce a non-generic base class though:
type Car() = class end type Car<'T> () = inherit Car() type Mercedes () = inherit Car<int> () let merc = Mercedes () box merc :? Car
To answer your first question, I think the compiler is giving you a hint that the operation is not useful because it will always succeed - so there is no point checking this using
:?
.If you instead have a value that has a static type of
Car<int>
and you want to check whether it isMercedes
, this is alowed, because that is an interesting question to ask:Car<int>() :? Mercedes
But checking
Car<obj>() :? Mercedes
is not allowed, because this is statically known to be false. -
answered 2022-01-26 06:34
Romain Deneau
To answer your first question:
Why do you need to box
merc
for the test againstCar<int>
, but not for the test againstMercedes
?From the docs, the type test operator
:?
Returns
true
if the value matches the specified type (including if it is a subtype); otherwise, returnsfalse
Line 4
box merc :? Car<int>
succeeds becausebox merc
rises the type in the hierarchy up toobject
, the highest type.Line 3
merc :? Car<int>
should have returnfalse
becauseMercedes
>Car<int>
. Instead, it's not compiling which is a behavior not documented but also not surprising for me as we always know that it cannot be true. Notice that it could have been just a warning, like in C# when we writesif (true) ...
.But it's surprising why this behavior is not applied for line 1
merc :? Mercedes
. It should fail to compile for the same reason of "obviousness". Instead, it's just a warningFS0067: This type test or cast of a base class into a derived class will always succeed
🤔👉 Conclusion: the type test operator is really designed just as a downcast test operator. Using it in other cases like to check upcast can be misleading (at compile time) but still safe at runtime.
do you know?
how many words do you know