Skip to content

Instantly share code, notes, and snippets.

@johnbfair
Last active August 29, 2015 14:00
Show Gist options
  • Select an option

  • Save johnbfair/11252434 to your computer and use it in GitHub Desktop.

Select an option

Save johnbfair/11252434 to your computer and use it in GitHub Desktop.
Trying to figure out how to retrieve the generic argument to an interface in F#
type ISample'<'T> =
abstract member Sample<'T> : unit -> unit
type SampleTest<'T>() =
interface ISample'<'T> with
member x.Sample<'T>() = ()
open System
open System.Reflection
// I have use cases for matching interfaces such as ISample and ISample<'T>
type IType =
| Interface of Type
| GenericInterface of Type
module InterfaceChecker =
let private checkGeneric (t:Type) = t.IsGenericType
let private existsGeneric (interfaceT:Type) (t:Type) = interfaceT = t.GetGenericTypeDefinition()
let private checkInterface (t:Type) = t.IsInterface
let private existsInterface (interfaceT:Type) (t:Type) = interfaceT = t
let checkInterfaceImplementation interfaceType (t:Type) =
let (check, exists) =
match interfaceType with
| Interface t' ->
(checkInterface, existsInterface t')
| GenericInterface t' ->
(checkGeneric, existsGeneric t')
t.GetInterfaces()
|> Array.filter check
|> Array.exists exists
module TypeSearcher =
// This accepts a List instead of Array because the caller expects to pass a List
let retrieveType<'T> interfaceType (typeList:Type list) =
let interfaceFilter = InterfaceChecker.checkInterfaceImplementation interfaceType
typeList
|> List.toArray
|> Array.filter interfaceFilter
|> Array.filter (fun t ->
match interfaceType with
| GenericInterface _ ->
// These are broken out to help step through the reflected values to see what's going on
// It could be expressed as: (typedefof<'T>).GetGenericArguments().[0].Name
let myType = typedefof<'T>
let gen = myType.GetGenericArguments()
gen.[0].Name = t.Name
| _ -> true)
// Module to mock out testing (so I don't have to add the NUnit and FsUnit dependencies here)
module ReflectionTester =
// If it could find the interface with matching generic type then we should get true back
let PassingTest() =
TypeSearcher.retrieveType<SampleTest<int>> (GenericInterface typedefof<ISample'<int>>) [typedefof<SampleTest<int>>]
|> Array.length >= 1
|> printfn "This should be true: %A"
// If it could *not* find the interface with matching generic type then we should get false back
let FailingTest() =
TypeSearcher.retrieveType<SampleTest<float>> (GenericInterface typedefof<ISample'<float>>) [typedefof<SampleTest<int>>]
|> Array.length >= 1
|> printfn "This should be false: %A"
type ISample<'T> =
abstract member Sample<'T> : unit -> unit
type SampleTest<'T>() =
interface ISample<'T> with
member x.Sample<'T>() = ()
open System
open System.Reflection
// I have use cases for matching interfaces such as ISample and ISample<'T>
type IType =
| Interface of Type
| GenericInterface of Type
module InterfaceChecker =
let private checkGeneric (t:Type) = t.IsGenericType
let private existsGeneric (interfaceT:Type) (t:Type) = interfaceT = t.GetGenericTypeDefinition()
let private checkInterface (t:Type) = t.IsInterface
let private existsInterface (interfaceT:Type) (t:Type) = interfaceT = t
let checkInterfaceImplementation interfaceType (t:Type) =
let (check, exists) =
match interfaceType with
| Interface t' ->
(checkInterface, existsInterface t')
| GenericInterface t' ->
(checkGeneric, existsGeneric t')
t.GetInterfaces()
|> Array.filter check
|> Array.exists exists
module TypeSearcher =
// This accepts a List instead of Array because the caller expects to pass a List
let retrieveType (t:Type) interfaceType (typeList:Type list) =
let interfaceFilter = InterfaceChecker.checkInterfaceImplementation interfaceType
typeList
|> List.toArray
|> Array.filter interfaceFilter
|> Array.filter (fun t' ->
match interfaceType with
| GenericInterface _ ->
t'.GetGenericArguments().[0].Name = t.GetGenericArguments().[0].Name
| _ -> true)
// Module to mock out testing (so I don't have to add the NUnit and FsUnit dependencies here)
module ReflectionTester =
// If it could find the interface with matching generic type then we should get true back
let PassingTest() =
TypeSearcher.retrieveType typedefof<SampleTest<int>> (GenericInterface typedefof<ISample<int>>) [typedefof<SampleTest<int>>]
|> Array.length >= 1
|> printfn "This should be true: %A"
// If it could *not* find the interface with matching generic type then we should get false back
let FailingTest() =
TypeSearcher.retrieveType typedefof<SampleTest<float>> (GenericInterface typedefof<ISample<float>>) [typedefof<SampleTest<int>>]
|> Array.length >= 1
|> printfn "This should be false: %A"
@johnbfair
Copy link
Author

Looks like lack of sleep was to blame here. Go back a version to see what was failing. Basically I wasn't comparing the generic arguments of both types (only one of them). So I was comparing "SampleTest" to "T" and it was failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment