Pattern matching on generics in a non-generic method implementing an interface

后端 未结 2 1160
心在旅途
心在旅途 2021-01-21 11:42

I have a frustrating problem. I\'m building a view engine in ASP.NET MVC and are implementing the interface IViewEngine. In one of the methods I\'m trying to dynamically figure

2条回答
  •  一个人的身影
    2021-01-21 12:31

    Unfortunately, there is no way to do this nicely. If you have control over the Template<'T> type, then the best option is to create a non-generic interface (e.g. ITemplate) and implement that in the Template<'T> type. Then you can just check for the interface:

    | :? ITemplate as t -> ...
    

    If that's not the case, then your only option is to use some reflection magic. You can implement an active pattern that matches when the type is some Template<'T> value and returns a list of types (System.Type objects) that were used as generic arguments. In your pseudo-code, you wanted to get this as the generic type parameter 'a - it isn't possible to get that as compile-time type parameter, but you can get that as runtime type information:

    let (|GenericTemplate|_|) l =
      let lty = typeof>.GetGenericTypeDefinition()
      let aty = l.GetType()
      if aty.IsGenericType && aty.GetGenericTypeDefinition() = lty then
        Some(aty.GetGenericArguments())
      else 
        None
    

    Now you can write the following pattern matching code:

    match result with
    | GenericTemplate tys ->
        // ...
    

    The last problem is - how can you use this runtime type information to run some generic code. The best option I can think of is to invoke a generic method (or a function) using reflection - then you can specify the runtime type information as a generic parameter and so the code can be generic. The simplest option is to invoke static member of a type:

     type TemplateHandler =
       static member Handle<'T>(arg:Template<'T>) =
         // This is a standard generic method that will be 
         // called from the pattern matching - you can write generic
         // body of the case here...
         "aaa"
    
    | :? GenericTemplate tys ->
        // Invoke generic method dynamically using reflection
        let gmet = typeof.GetMethod("Handle").MakeGenericMethod(tys)
        gmet.Invoke(null, [| result |]) :?> string // Cast the result to some type
    

    The key idea is that you move body of a pattern matching (which cannot have generic type parameters) into a method (that can have generic type parameters) and run the method dynamically using reflection.

    You could change the code to use let function instead of static member as well - it is only slightly more difficult to find the function using reflection.

提交回复
热议问题