match value with
| :? list<#SomeType> as l -> l //Is it possible to match any list of a type derived from SomeType?
| _ -> failwith "doesn't match"
As already pointed out, there is no way to do this directly (pattern matching can only bind values, but it cannot bind new type variables). In addition to the (more general) workaround by kvb you can use the fact that all collections implement non-generic IEnumerable
, so you can check for this type:
match box value with
| :? System.Collections.IEnumerable as l when
// assumes that the actual type of 'l' is 'List<T>' or some other type
// with single generic type parameter (this is not fully correct, because
// it could be other type too, but we can ignore this for now)
typedefof<SomeType>.IsAssignableFrom
(value.GetType().GetGenericArguments().[0]) ->
l |> Seq.cast<SomeType>
| _ -> failwith "doesn't match"
The code tests whether the value is a non-generic IEnumerable
and whether the type parameter is subtype of SomeType
. In that case, we got a list of some derived type, so we can cast it to a sequence of SomeType
values (this is slightly different than working with list of values of the derived types, but it shouldn't matter for practical purposes).
No, it's unfortunately not possible to do something like this - the CLR doesn't provide any efficient way of doing that kind of type test. See How to cast an object to a list of generic type in F# and F# and pattern matching on generics in a non-generic method implementing an interface for a few (rather ugly) solutions.
I later needed something similar for matching Lazy instances. Here's my solution, in case anyone finds it helpful.
let (|Lazy|_|) (value : obj) =
if box value <> null then
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<Lazy<_>> then
Some(typ.GetGenericArguments().[0])
else None
else None
Usage:
match value with
| Lazy typ when typeof<SomeType>.IsAssignableFrom(typ) -> (value :?> Lazy<_>).Value
| _ -> failwith "not an instance of Lazy<#SomeType>"
According to the F# 2.0 specification, par. 14.5.2 (Solving Subtype Constraints), it will not work, because: "F# generic types do not support covariance or contravariance."
Not the cleanest, but effective:
let matchType<'T> () =
try
let o = Activator.CreateInstance<'T> ()
match box o with
| :? Type1 -> printfn "Type1"
| :? Type2 -> printfn "Type2"
| _ -> failwith "unknown type"
with
| ex -> failwith "%s" (ex.ToString())
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