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

后端 未结 2 1159
心在旅途
心在旅途 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:27

    Can you make the whole view engine class generic, according to the type of 'key it uses? Indivudual projects will need to inherit from your view engine class and specify the type of key in the process.

    0 讨论(0)
  • 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<list<int>>.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<TemplateHandler>.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.

    0 讨论(0)
提交回复
热议问题