Last active
August 29, 2015 14:00
-
-
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#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.