Reputation: 378
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
merc
for the test against Car<int>
, but not for the test against Mercedes
?true
because merc
is a Car
, without regard to the type argument. Is there such a thing?Upvotes: 1
Views: 58
Reputation: 3061
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 because box merc
rises the type in the hierarchy up to object
, the highest type.
Line 3 merc :? Car<int>
should have return false
because Mercedes
> 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 writes if (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 warning FS0067: 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.
Upvotes: 0
Reputation: 243051
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 of Car<_>
:
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 is Mercedes
, 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.
Upvotes: 2