Предложения теряются после преобразования в делегат
С F# полученным шаблоном MethodWithReflectedDefinition
мы можем получить объект цитаты из MethodInfo
, Но если мы преобразуем функцию F# или метод-член в делегат, мы не сможем получить цитаты, даже если эта функция или метод помечены [<ReflectedDefinition>]
, Вот пример:
open System
open System.Reflection
open System.Runtime.CompilerServices
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open NUnit.Framework
let printQuotation (methodInfo:MethodInfo) =
match methodInfo with
| MethodWithReflectedDefinition expr -> printfn "%A" expr
| _ -> printfn "Cannot get quotations."
let printQuotationFromDelegate (del:Func<int, int>) =
printQuotation del.Method
[<ReflectedDefinition>]
let foo a = a + 1
type MyClass private () =
[<ReflectedDefinition>]
static member Foo(a:int) =
a + 1
[<Test>]
let test() =
let methodInfo = typeof<MyClass>.GetMethod("Foo", BindingFlags.Static ||| BindingFlags.Public)
printQuotation methodInfo // works
// cannot get quotations
printQuotationFromDelegate(Func<_,_> MyClass.Foo)
// cannot get quotations
printQuotationFromDelegate(Func<_,_> foo)
Если я проверяю код сборки IL, я обнаружил, что причина в том, что foo
значение и MyType.Foo
метод есть [ReflectedDefinition]
атрибут:
.method public static
int32 Foo (
int32 a
) cil managed
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.ReflectedDefinitionAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x2150
// Code size 5 (0x5)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.1
IL_0003: add
IL_0004: ret
} // end of method MyClass::Foo
Но преобразованный делегат не использует эти функции, он использует сгенерированный метод, который является копией исходного метода. Но, к сожалению, эти сгенерированные функции не имеют такого ReflectedDefinition
атрибут, и я думаю, что цитаты не связаны с этим сгенерированным методом:
IL_0021: newobj instance void QuotationInDelegate/test@33::.ctor()
IL_0026: ldftn instance int32 QuotationInDelegate/test@33::Invoke(int32)
IL_002c: newobj instance void class [mscorlib]System.Func`2<int32, int32>::.ctor(object, native int)
IL_0031: call void QuotationInDelegate::printQuotationFromDelegate(class [mscorlib]System.Func`2<int32, int32>)
и сгенерированный метод test@33:Invoke
:
.class nested assembly auto auto sealed specialname serializable beforefieldinit test@33
extends [mscorlib]System.Object
{
.custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = (
01 00 06 00 00 00 00 00
)
// Methods
.method public specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2158
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method test@33::.ctor
.method assembly hidebysig
instance int32 Invoke (
int32 arg
) cil managed
{
// Method begins at RVA 0x2160
// Code size 8 (0x8)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: call int32 QuotationInDelegate/MyClass::Foo(int32)
IL_0007: ret
} // end of method test@33::Invoke
} // end of class test@33
Итак, как мы можем сохранить информацию предложения после ее преобразования в делегат?