Как получить котировку F # для метода экземпляра интерфейса?

В F# мы можем создать экземпляр интерфейса по объектному выражению, но пока я пытаюсь использовать атрибут ReflectedDefinition в методе экземпляра, я не могу получить котировки. Информация о методе объявляется в типе интерфейса, а не в типе экземпляра.

Вот мой тестовый код:

module Test

open System
open System.Reflection
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.ExprShape

type IMyInterface =
    abstract Foo : int -> int

let createMyInterface () =
    { new IMyInterface with
        [<ReflectedDefinition>]
        member this.Foo a = a + 1 }

let expr =
    let a = createMyInterface()
    <@ a.Foo(42) @>

let rec iterExpr (expr:Expr) =
    match expr with
    | Call(objectExpr, info, paramExprs) ->
        printfn "info: %A" info
        printfn "reflected type: %A" info.ReflectedType
        match info with
        | MethodWithReflectedDefinition methodExpr ->
            printfn "%A" methodExpr
        | _ -> failwith "No reflected definition"

    | ShapeVar _ -> failwithf "TODO: %A" expr
    | ShapeLambda _ -> failwithf "TODO: %A" expr
    | ShapeCombination _ -> failwithf "TODO: %A" expr

let test() =
    iterExpr expr

[<EntryPoint>]
let main argv = 
    test()
    0 // return an integer exit code

Если я запустил его, я получил исключение:

C:\Users\Xiang\Documents\Inbox\TTTT\bin\Debug>TTTT
info: Int32 Foo(Int32)
reflected type: Test+IMyInterface

Unhandled Exception: System.Exception: No reflected definition
   at Microsoft.FSharp.Core.Operators.FailWith[T](String message)
   at Test.iterExpr(FSharpExpr expr) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 30
   at Test.test() in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 37
   at Test.main(String[] argv) in C:\Users\Xiang\Documents\Inbox\TTTT\Program.fs:line 41

А еще я проверил сгенерированную сборку с помощью dotPeek, она реализована как производный класс:

[CompilationMapping(SourceConstructFlags.ObjectType)]
  [Serializable]
  public interface IMyInterface
  {
    int Foo([In] int obj0);
  }

  [CompilationMapping(SourceConstructFlags.Closure)]
  [Serializable]
  [SpecialName]
  [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
  internal sealed class createMyInterface\u004014 : Test.IMyInterface
  {
    public createMyInterface\u004014()
    {
      base.\u002Ector();
      Test.createMyInterface\u004014 createMyInterface14 = this;
    }

    [ReflectedDefinition]
    int Test.IMyInterface.Test\u002DIMyInterface\u002DFoo([In] int obj0)
    {
      return obj0 + 1;
    }
  }

Итак, проблема в том, что когда я вызываю метод Foo в кавычках, шаблон вызова получает MethodInfo, который объявлен в типе интерфейса, который не имеет определения. Итак, как я могу получить настоящую реализацию MethodInfo? и тогда я могу получить цитату реализации?


person Xiang Zhang    schedule 02.06.2014    source источник


Ответы (1)


Вот вкратце ваша проблема:

  1. Вы вызываете виртуальный метод через экземпляр типа, в котором метод определен.
  2. Вы хотите, чтобы цитата содержала вызов метода, определенного в производном классе.

Это не сработает и не ограничивается интерфейсами или объектными выражениями:

type A() = 
    abstract M : unit -> unit
    default this.M() = printfn "abstract"

type T() =
    inherit A() with
        [<ReflectedDefinition>]
        override this.M() = printfn "override"

let expr =
    let a : A = upcast T()
    <@ a.M() @>

По сути, весь смысл объектного выражения состоит в том, чтобы предоставить анонимную реализацию незапечатанного класса, поэтому то, о чем вы просите, не имеет для меня смысла - компилятор знает только, что объект - это некоторый экземпляр, реализующий этот интерфейс но не может знать конкретный тип экземпляра и, следовательно, не может знать, какой (из потенциально многих) конкретный метод использовать.

person kvb    schedule 02.06.2014
comment
Спасибо, да, я знаю, это виртуальная функция. Но у виртуальной функции должен быть способ найти конкретную реализацию, иначе она не может быть выполнена. И что я хочу, так это то, что я хочу знать способ найти конкретную реализацию с учетом информации о методе виртуальной функции, но не для ее выполнения, мне нужно получить ее цитату. Вариант использования: я пытаюсь использовать эту функцию в цитате для кода кода GPU, который я не хочу выполнять виртуальную функцию, но хочу получить цитату и сгенерировать код GPU на ее основе. У вас есть идеи, как это сделать? - person Xiang Zhang; 03.06.2014
comment
Нет, компилятор F# не может узнать тип среды выполнения при создании цитаты (цитата создается на основе содержащегося в ней синтаксического дерева, которое не зависит от значения среды выполнения). Вы можете сделать некоторые хакерские вещи во время выполнения, чтобы попытаться заставить его работать, но кажется, что, возможно, интерфейс - это неправильный способ делать то, что вы хотите - почему бы вместо этого не использовать не виртуальный метод? - person kvb; 03.06.2014
comment
у не-виртуального нет проблем, объектное выражение - очень хороший способ. И да, я согласен с вами, цитата находится во время компиляции, она понятия не имеет о вещах во время выполнения. И я подумал некоторое время, я понял, что мне может понадобиться проверить что-то в objectExpr в шаблоне Call, если objectExpr является значением, то я могу получить его тип времени выполнения, который должен быть конкретным типом. Спасибо за Ваш ответ! - person Xiang Zhang; 03.06.2014